From fa0de18ffed2ba9a24de124db589879adab8b1ed Mon Sep 17 00:00:00 2001 From: Inusaito Sayori Date: Sat, 7 Jun 2014 06:27:36 -0400 Subject: [PATCH] [Voice Update] Small sync, less warning spam is likely Should fix group chats sometimes not showing anyone. (Seemed to for me! ^*^) --- indra/newview/app_settings/settings.xml | 22 +++++ indra/newview/llspeakers.cpp | 116 ++++++++++++++++++++++-- indra/newview/llspeakers.h | 14 +++ indra/newview/llvoicechannel.cpp | 7 +- indra/newview/llvoicechannel.h | 4 +- indra/newview/llvoiceclient.cpp | 4 +- indra/newview/llvoicevivox.cpp | 40 ++++---- indra/newview/llvoicevivox.h | 4 +- 8 files changed, 179 insertions(+), 32 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 3cdca67a0..a126b13ce 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4168,6 +4168,28 @@ This should be as low as possible, but too low may break functionality Value 0 + ChatLoadGroupMaxMembers + + Comment + Max number of active members we'll show up for an unresponsive group + Persist + 1 + Type + S32 + Value + 100 + + ChatLoadGroupTimeout + + Comment + Time we give the server to send group participants before we hit the server for group info (seconds) + Persist + 1 + Type + F32 + Value + 10.0 + ChatOnlineNotification Comment diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 215e9f242..6af80af68 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -32,6 +32,7 @@ #include "llavatarnamecache.h" #include "llimpanel.h" // For LLFloaterIMPanel #include "llimview.h" +#include "llgroupmgr.h" #include "llsdutil.h" #include "llui.h" #include "llviewerobjectlist.h" @@ -61,8 +62,7 @@ LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerTy mModeratorMutedVoice(FALSE), mModeratorMutedText(FALSE) { - // Make sure we also get the display name if SLIM or some other external voice client is used and not whatever is provided. - if ((name.empty() && type == SPEAKER_AGENT) || type == SPEAKER_EXTERNAL) + if (name.empty() && type == SPEAKER_AGENT) { lookupName(); } @@ -100,6 +100,19 @@ 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), @@ -257,6 +270,11 @@ bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_ return true; } +bool LLSpeakersDelayActionsStorage::isTimerStarted(const LLUUID& speaker_id) +{ + return (mActionTimersMap.size() > 0) && (mActionTimersMap.find(speaker_id) != mActionTimersMap.end()); +} + // // ModerationResponder // @@ -269,9 +287,9 @@ public: mSessionID = session_id; } - /*virtual*/ void error(U32 status, const std::string& reason) + virtual void error(U32 status, const std::string& reason) { - llwarns << "ModerationResponder error [status:" << status << "]: " << reason << llendl; + llwarns << status << ": " << reason << llendl; if ( gIMMgr ) { @@ -308,8 +326,10 @@ private: LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) : mVoiceChannel(channelp), mVoiceModerated(false), - mModerateModeHandledFirstTime(false) + mModerateModeHandledFirstTime(false), + mSpeakerListUpdated(false) { + mGetListTime.reset(); static LLUICachedControl remove_delay ("SpeakerParticipantRemoveDelay", 10.0); mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay); @@ -322,7 +342,8 @@ LLSpeakerMgr::~LLSpeakerMgr() LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) { - if (id.isNull()) + LLUUID session_id = getSessionID(); + if (id.isNull() || (id == session_id)) { return NULL; } @@ -434,6 +455,7 @@ void LLSpeakerMgr::update(BOOL resort_ok) { 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 @@ -522,6 +544,81 @@ void LLSpeakerMgr::updateSpeakerList() (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) + /* Singu TODO: LLIMModel::LLIMSession + LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id); + if (session->isGroupSessionType() && (mSpeakers.size() <= 1)) + */ + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(session_id); + if (floater && floater->getSessionType() == LLFloaterIMPanel::GROUP_SESSION && (mSpeakers.size() <= 1)) + { + const F32 load_group_timeout = gSavedSettings.getF32("ChatLoadGroupTimeout"); + // 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 && (mGetListTime.getElapsedTimeF32() >= load_group_timeout)) + { + // Request the data the first time around + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(session_id); + } + else 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 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; + } + } + /* Singu TODO: LLIMModel::LLIMSession + else if (mSpeakers.size() == 0) + { + // For all other session type (ad-hoc, P2P, avaline), 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(LLSpeaker* speakerp) @@ -586,6 +683,10 @@ 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) { @@ -604,6 +705,7 @@ void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) { speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); speakerp->mHasSpoken = TRUE; + fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); } } @@ -956,7 +1058,7 @@ void LLLocalSpeakerMgr::updateSpeakerList() if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) { LLVOAvatar* avatarp = gObjectList.findAvatar(speaker_id); - if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS_SQUARED) + if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS * CHAT_NORMAL_RADIUS) { setSpeakerNotInChannel(speakerp); } diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index 1d58d1b1d..107749224 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -81,6 +81,15 @@ public: BOOL mModeratorMutedText; }; +class LLSpeakerUpdateSpeakerEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerUpdateSpeakerEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +private: + const LLUUID& mSpeakerID; +}; + class LLSpeakerUpdateModeratorEvent : public LLOldEvents::LLEvent { public: @@ -186,6 +195,8 @@ public: void unsetActionTimer(const LLUUID& speaker_id); void removeAllTimers(); + + bool isTimerStarted(const LLUUID& speaker_id); private: /** * Callback of the each instance of LLSpeakerActionTimer. @@ -230,6 +241,7 @@ public: void getSpeakerList(speaker_list_t* speaker_list, BOOL include_text); LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; } const LLUUID getSessionID(); + bool isSpeakerToBeRemoved(const LLUUID& speaker_id); /** * Removes avaline speaker. @@ -253,6 +265,8 @@ protected: typedef std::map > speaker_map_t; speaker_map_t mSpeakers; + bool mSpeakerListUpdated; + LLTimer mGetListTime; speaker_list_t mSpeakersSorted; LLFrameTimer mSpeechTimer; diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 2fd7bb572..d8b96b6ee 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -55,8 +55,9 @@ class LLVoiceCallCapResponder : public LLHTTPClient::ResponderWithResult public: LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {}; - /*virtual*/ void error(U32 status, const std::string& reason); // called with bad status codes - /*virtual*/ void result(const LLSD& content); + // called with bad status codes + virtual void error(U32 status, const std::string& reason); + virtual void result(const LLSD& content); /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return voiceCallCapResponder_timeout; } /*virtual*/ char const* getName(void) const { return "LLVoiceCallCapResponder"; } @@ -412,7 +413,7 @@ void LLVoiceChannel::doSetState(const EState& new_state) mState = new_state; if (!mStateChangedCallback.empty()) - mStateChangedCallback(old_state, mState, mCallDirection, mCallEndedByAgent); + mStateChangedCallback(old_state, mState, mCallDirection, mCallEndedByAgent, mSessionID); } //static diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 55dcf0f07..cf389d971 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -49,7 +49,7 @@ public: OUTGOING_CALL } EDirection; - typedef boost::signals2::signal state_changed_signal_t; + typedef boost::signals2::signal state_changed_signal_t; // on current channel changed signal typedef boost::function channel_changed_callback_t; @@ -58,7 +58,6 @@ public: static boost::signals2::connection setCurrentVoiceChannelChangedCallback(channel_changed_callback_t cb, bool at_front = false); - LLVoiceChannel(const LLUUID& session_id, const std::string& session_name); virtual ~LLVoiceChannel(); @@ -202,4 +201,3 @@ private: }; #endif // LL_VOICECHANNEL_H - diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index f3709450d..54ca806de 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -112,8 +112,8 @@ LLVoiceClient::LLVoiceClient() : mVoiceModule(NULL), m_servicePump(NULL), - mVoiceEffectEnabled(LLCachedControl(gSavedSettings, "VoiceMorphingEnabled")), - mVoiceEffectDefault(LLCachedControl(gSavedPerAccountSettings, "VoiceEffectDefault")), + mVoiceEffectEnabled(LLCachedControl(gSavedSettings, "VoiceMorphingEnabled", true)), + mVoiceEffectDefault(LLCachedControl(gSavedPerAccountSettings, "VoiceEffectDefault", "00000000-0000-0000-0000-000000000000")), mPTTDirty(true), mPTT(true), mUsePTT(true), diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 58f8dbcf0..19d2c4997 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -295,6 +295,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mSessionTerminateRequested(false), mRelogRequested(false), mConnected(false), + mTerminateDaemon(false), mPump(NULL), mSpatialJoiningNum(0), @@ -567,25 +568,25 @@ void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries) { LLViewerRegion *region = gAgent.getRegion(); - if ( region && (mVoiceEnabled || !mIsInitialized)) + // If we've not received the capability yet, return. + // the password will remain empty, so we'll remain in + // stateIdle + if ( region && + region->capabilitiesReceived() && + (mVoiceEnabled || !mIsInitialized)) { std::string url = region->getCapability("ProvisionVoiceAccountRequest"); - if ( url.empty() ) + if ( !url.empty() ) { - // we've not received the capability yet, so return. - // the password will remain empty, so we'll remain in - // stateIdle - return; + LLHTTPClient::post( + url, + LLSD(), + new LLVivoxVoiceAccountProvisionResponder(retries)); + + setState(stateConnectorStart); } - - LLHTTPClient::post( - url, - LLSD(), - new LLVivoxVoiceAccountProvisionResponder(retries)); - - setState(stateConnectorStart); } } @@ -738,7 +739,7 @@ void LLVivoxVoiceClient::stateMachine() setVoiceEnabled(false); } - if(mVoiceEnabled || !mIsInitialized) + if(mVoiceEnabled || (!mIsInitialized && !mTerminateDaemon) ) { updatePosition(); } @@ -751,11 +752,12 @@ void LLVivoxVoiceClient::stateMachine() if((getState() != stateDisabled) && (getState() != stateDisableCleanup)) { // User turned off voice support. Send the cleanup messages, close the socket, and reset. - if(!mConnected) + if(!mConnected || mTerminateDaemon) { // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill. LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL; killGateway(); + mTerminateDaemon = false; } logout(); @@ -796,7 +798,7 @@ void LLVivoxVoiceClient::stateMachine() // Voice is locked out, we must not launch the vivox daemon. setState(stateJail); } - else if(!isGatewayRunning()) + else if(!isGatewayRunning() && gSavedSettings.getBOOL("EnableVoiceChat")) { if (true) // production build, not test { @@ -1281,6 +1283,7 @@ void LLVivoxVoiceClient::stateMachine() std::stringstream errs; errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; args["HOSTID"] = errs.str(); + mTerminateDaemon = true; LLNotificationsUtil::add("NoVoiceConnect", args); } else @@ -2759,6 +2762,7 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st std::stringstream errs; errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; args["HOSTID"] = errs.str(); + mTerminateDaemon = true; LLNotificationsUtil::add("NoVoiceConnect", args); } else @@ -2767,6 +2771,7 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL; mVoiceVersion.serverVersion = versionID; mConnectorHandle = connectorHandle; + mTerminateDaemon = false; if(getState() == stateConnectorStarting) { setState(stateConnectorStarted); @@ -6936,6 +6941,9 @@ void LLVivoxProtocolParser::processResponse(std::string tag) */ // We don't need to process this, but we also shouldn't warn on it, since that confuses people. } + else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent")) + { // Yet another ignored event + } else { LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 03811e4e1..3843bbd7e 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -111,7 +111,7 @@ public: //////////////////////////// /// @name Channel stuff //@{ - // returns true if the user is currently in a proximal (local spatial) channel. + // returns true iff the user is currently in a proximal (local spatial) channel. // Note that gestures should only fire if this returns true. virtual bool inProximalChannel(); @@ -640,6 +640,8 @@ private: LLSocket::ptr_t mSocket; bool mConnected; + // We should kill the voice daemon in case of connection alert + bool mTerminateDaemon; LLPumpIO *mPump; friend class LLVivoxProtocolParser;