phoenix-firestorm/indra/newview/llcallingcard.cpp

1080 lines
32 KiB
C++

/**
* @file llcallingcard.cpp
* @brief Implementation of the LLPreviewCallingCard class
*
* $LicenseInfo:firstyear=2002&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"
#if LL_WINDOWS
#pragma warning( disable : 4800 ) // performance warning in <functional>
#endif
#include "llcallingcard.h"
#include <algorithm>
#include "indra_constants.h"
//#include "llcachename.h"
#include "llstl.h"
#include "lltimer.h"
#include "lluuid.h"
#include "message.h"
#include "llagent.h"
#include "llavatarnamecache.h"
#include "llinventoryobserver.h"
#include "llinventorymodel.h"
#include "llnotifications.h"
#include "llslurl.h"
#include "llimview.h"
#include "lltrans.h"
#include "llviewercontrol.h"
#include "llviewerobjectlist.h"
#include "llvoavatar.h"
#include "llavataractions.h"
#include "lluiusage.h"
// Firestorm includes
#include "fscommon.h"
#include "fsfloaternearbychat.h"
#include "fskeywords.h"
#include "lggcontactsets.h"
#include "llfloaterreg.h"
#include "llnotificationmanager.h"
///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------
class LLTrackingData
{
public:
LLTrackingData(const LLUUID& avatar_id, const std::string& name);
bool haveTrackingInfo();
void setTrackedCoarseLocation(const LLVector3d& global_pos);
void agentFound(const LLUUID& prey,
const LLVector3d& estimated_global_pos);
public:
LLUUID mAvatarID;
std::string mName;
LLVector3d mGlobalPositionEstimate;
bool mHaveInfo;
bool mHaveCoarseInfo;
LLTimer mCoarseLocationTimer;
LLTimer mUpdateTimer;
LLTimer mAgentGone;
};
const F32 COARSE_FREQUENCY = 2.2f;
const F32 FIND_FREQUENCY = 29.7f; // This results in a database query, so cut these back
const F32 OFFLINE_SECONDS = FIND_FREQUENCY + 8.0f;
// static
LLAvatarTracker LLAvatarTracker::sInstance;
static void on_avatar_name_cache_notify(const LLUUID& agent_id,
const LLAvatarName& av_name,
bool online,
LLSD payload);
///----------------------------------------------------------------------------
/// Class LLAvatarTracker
///----------------------------------------------------------------------------
LLAvatarTracker::LLAvatarTracker() :
mTrackingData(NULL),
mTrackedAgentValid(false),
mModifyMask(0x0),
mIsNotifyObservers(false)
{
}
LLAvatarTracker::~LLAvatarTracker()
{
deleteTrackingData();
std::for_each(mObservers.begin(), mObservers.end(), DeletePointer());
mObservers.clear();
std::for_each(mBuddyInfo.begin(), mBuddyInfo.end(), DeletePairedPointer());
mBuddyInfo.clear();
}
void LLAvatarTracker::track(const LLUUID& avatar_id, const std::string& name)
{
deleteTrackingData();
mTrackedAgentValid = false;
mTrackingData = new LLTrackingData(avatar_id, name);
findAgent();
// We track here because findAgent() is called on a timer (for now).
if(avatar_id.notNull())
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_TrackAgent);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_TargetData);
msg->addUUIDFast(_PREHASH_PreyID, avatar_id);
gAgent.sendReliableMessage();
}
}
void LLAvatarTracker::untrack(const LLUUID& avatar_id)
{
if (mTrackingData && mTrackingData->mAvatarID == avatar_id)
{
deleteTrackingData();
mTrackedAgentValid = false;
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_TrackAgent);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_TargetData);
msg->addUUIDFast(_PREHASH_PreyID, LLUUID::null);
gAgent.sendReliableMessage();
}
}
void LLAvatarTracker::setTrackedCoarseLocation(const LLVector3d& global_pos)
{
if(mTrackingData)
{
mTrackingData->setTrackedCoarseLocation(global_pos);
}
}
bool LLAvatarTracker::haveTrackingInfo()
{
if(mTrackingData)
{
return mTrackingData->haveTrackingInfo();
}
return false;
}
LLVector3d LLAvatarTracker::getGlobalPos()
{
if(!mTrackedAgentValid || !mTrackingData) return LLVector3d();
LLVector3d global_pos;
LLViewerObject* object = gObjectList.findObject(mTrackingData->mAvatarID);
if(object && !object->isDead())
{
global_pos = object->getPositionGlobal();
// HACK - for making the tracker point above the avatar's head
// rather than its groin
LLVOAvatar* av = (LLVOAvatar*)object;
global_pos.mdV[VZ] += 0.7f * (av->mBodySize.mV[VZ] + av->mAvatarOffset.mV[VZ]);
mTrackingData->mGlobalPositionEstimate = global_pos;
}
else
{
global_pos = mTrackingData->mGlobalPositionEstimate;
}
return global_pos;
}
void LLAvatarTracker::getDegreesAndDist(F32& rot,
F64& horiz_dist,
F64& vert_dist)
{
if(!mTrackingData) return;
LLVector3d global_pos;
LLViewerObject* object = gObjectList.findObject(mTrackingData->mAvatarID);
if(object && !object->isDead())
{
global_pos = object->getPositionGlobal();
mTrackingData->mGlobalPositionEstimate = global_pos;
}
else
{
global_pos = mTrackingData->mGlobalPositionEstimate;
}
LLVector3d to_vec = global_pos - gAgent.getPositionGlobal();
horiz_dist = sqrt(to_vec.mdV[VX] * to_vec.mdV[VX] + to_vec.mdV[VY] * to_vec.mdV[VY]);
vert_dist = to_vec.mdV[VZ];
rot = F32(RAD_TO_DEG * atan2(to_vec.mdV[VY], to_vec.mdV[VX]));
}
const std::string& LLAvatarTracker::getName()
{
if(mTrackingData)
{
return mTrackingData->mName;
}
else
{
return LLStringUtil::null;
}
}
const LLUUID& LLAvatarTracker::getAvatarID()
{
if(mTrackingData)
{
return mTrackingData->mAvatarID;
}
else
{
return LLUUID::null;
}
}
S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds)
{
using namespace std;
U32 new_buddy_count = 0;
LLUUID agent_id;
for(buddy_map_t::const_iterator itr = buds.begin(); itr != buds.end(); ++itr)
{
agent_id = (*itr).first;
buddy_map_t::const_iterator existing_buddy = mBuddyInfo.find(agent_id);
if(existing_buddy == mBuddyInfo.end())
{
++new_buddy_count;
mBuddyInfo[agent_id] = (*itr).second;
// pre-request name for notifications?
LLAvatarName av_name;
LLAvatarNameCache::get(agent_id, &av_name);
addChangedMask(LLFriendObserver::ADD, agent_id);
LL_DEBUGS() << "Added buddy " << agent_id
<< ", " << (mBuddyInfo[agent_id]->isOnline() ? "Online" : "Offline")
<< ", TO: " << mBuddyInfo[agent_id]->getRightsGrantedTo()
<< ", FROM: " << mBuddyInfo[agent_id]->getRightsGrantedFrom()
<< LL_ENDL;
}
else
{
LLRelationship* e_r = (*existing_buddy).second;
LLRelationship* n_r = (*itr).second;
LL_WARNS() << "!! Add buddy for existing buddy: " << agent_id
<< " [" << (e_r->isOnline() ? "Online" : "Offline") << "->" << (n_r->isOnline() ? "Online" : "Offline")
<< ", " << e_r->getRightsGrantedTo() << "->" << n_r->getRightsGrantedTo()
<< ", " << e_r->getRightsGrantedTo() << "->" << n_r->getRightsGrantedTo()
<< "]" << LL_ENDL;
}
}
// do not notify observers here - list can be large so let it be done on idle.
return new_buddy_count;
}
void LLAvatarTracker::copyBuddyList(buddy_map_t& buddies) const
{
buddy_map_t::const_iterator it = mBuddyInfo.begin();
buddy_map_t::const_iterator end = mBuddyInfo.end();
for(; it != end; ++it)
{
buddies[(*it).first] = (*it).second;
}
}
void LLAvatarTracker::terminateBuddy(const LLUUID& id)
{
LL_DEBUGS() << "LLAvatarTracker::terminateBuddy()" << LL_ENDL;
LLUIUsage::instance().logCommand("Agent.TerminateFriendship");
LLRelationship* buddy = get_ptr_in_map(mBuddyInfo, id);
if(!buddy) return;
mBuddyInfo.erase(id);
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("TerminateFriendship");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID());
msg->addUUID("SessionID", gAgent.getSessionID());
msg->nextBlock("ExBlock");
msg->addUUID("OtherID", id);
gAgent.sendReliableMessage();
addChangedMask(LLFriendObserver::REMOVE, id);
delete buddy;
}
// get all buddy info
const LLRelationship* LLAvatarTracker::getBuddyInfo(const LLUUID& id) const
{
if(id.isNull()) return NULL;
return get_ptr_in_map(mBuddyInfo, id);
}
bool LLAvatarTracker::isBuddy(const LLUUID& id) const
{
LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
return (info != NULL);
}
// online status
void LLAvatarTracker::setBuddyOnline(const LLUUID& id, bool is_online)
{
LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
if(info)
{
info->online(is_online);
addChangedMask(LLFriendObserver::ONLINE, id);
LL_DEBUGS() << "Set buddy " << id << (is_online ? " Online" : " Offline") << LL_ENDL;
}
else
{
//<FS:LO> Fix possible log spam with a large friendslist when SL messes up.
//LL_WARNS() << "!! No buddy info found for " << id
LL_DEBUGS() << "!! No buddy info found for " << id
<< ", setting to " << (is_online ? "Online" : "Offline") << LL_ENDL;
//</FS:LO>
}
}
bool LLAvatarTracker::isBuddyOnline(const LLUUID& id) const
{
LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
if(info)
{
return info->isOnline();
}
return false;
}
// empowered status
void LLAvatarTracker::setBuddyEmpowered(const LLUUID& id, bool is_empowered)
{
LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
if(info)
{
info->grantRights(LLRelationship::GRANT_MODIFY_OBJECTS, 0);
mModifyMask |= LLFriendObserver::POWERS;
}
}
bool LLAvatarTracker::isBuddyEmpowered(const LLUUID& id) const
{
LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
if(info)
{
return info->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS);
}
return false;
}
void LLAvatarTracker::empower(const LLUUID& id, bool grant)
{
// wrapper for ease of use in some situations.
buddy_map_t list;
/*
list.insert(id);
empowerList(list, grant);
*/
}
void LLAvatarTracker::empowerList(const buddy_map_t& list, bool grant)
{
LL_WARNS() << "LLAvatarTracker::empowerList() not implemented." << LL_ENDL;
/*
LLMessageSystem* msg = gMessageSystem;
const char* message_name;
const char* block_name;
const char* field_name;
if(grant)
{
message_name = _PREHASH_GrantModification;
block_name = _PREHASH_EmpoweredBlock;
field_name = _PREHASH_EmpoweredID;
}
else
{
message_name = _PREHASH_RevokeModification;
block_name = _PREHASH_RevokedBlock;
field_name = _PREHASH_RevokedID;
}
std::string name;
gAgent.buildFullnameAndTitle(name);
bool start_new_message = true;
buddy_list_t::const_iterator it = list.begin();
buddy_list_t::const_iterator end = list.end();
for(; it != end; ++it)
{
if(NULL == get_ptr_in_map(mBuddyInfo, (*it))) continue;
setBuddyEmpowered((*it), grant);
if(start_new_message)
{
start_new_message = false;
msg->newMessageFast(message_name);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->addStringFast(_PREHASH_GranterName, name);
}
msg->nextBlockFast(block_name);
msg->addUUIDFast(field_name, (*it));
if(msg->isSendFullFast(block_name))
{
start_new_message = true;
gAgent.sendReliableMessage();
}
}
if(!start_new_message)
{
gAgent.sendReliableMessage();
}
*/
}
void LLAvatarTracker::deleteTrackingData()
{
//make sure mTrackingData never points to freed memory
LLTrackingData* tmp = mTrackingData;
mTrackingData = NULL;
delete tmp;
}
void LLAvatarTracker::findAgent()
{
if (!mTrackingData) return;
if (mTrackingData->mAvatarID.isNull()) return;
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_FindAgent); // Request
msg->nextBlockFast(_PREHASH_AgentBlock);
msg->addUUIDFast(_PREHASH_Hunter, gAgentID);
msg->addUUIDFast(_PREHASH_Prey, mTrackingData->mAvatarID);
msg->addU32Fast(_PREHASH_SpaceIP, 0); // will get filled in by simulator
msg->nextBlockFast(_PREHASH_LocationBlock);
const F64 NO_LOCATION = 0.0;
msg->addF64Fast(_PREHASH_GlobalX, NO_LOCATION);
msg->addF64Fast(_PREHASH_GlobalY, NO_LOCATION);
gAgent.sendReliableMessage();
}
void LLAvatarTracker::addObserver(LLFriendObserver* observer)
{
if(observer)
{
mObservers.push_back(observer);
}
}
void LLAvatarTracker::removeObserver(LLFriendObserver* observer)
{
mObservers.erase(
std::remove(mObservers.begin(), mObservers.end(), observer),
mObservers.end());
}
void LLAvatarTracker::idleNotifyObservers()
{
if (mModifyMask == LLFriendObserver::NONE && mChangedBuddyIDs.size() == 0)
{
return;
}
notifyObservers();
}
void LLAvatarTracker::notifyObservers()
{
if (mIsNotifyObservers)
{
// Don't allow multiple calls.
// new masks and ids will be processed later from idle.
return;
}
LL_PROFILE_ZONE_SCOPED
mIsNotifyObservers = true;
observer_list_t observers(mObservers);
observer_list_t::iterator it = observers.begin();
observer_list_t::iterator end = observers.end();
for(; it != end; ++it)
{
(*it)->changed(mModifyMask);
}
for (changed_buddy_t::iterator it = mChangedBuddyIDs.begin(); it != mChangedBuddyIDs.end(); it++)
{
notifyParticularFriendObservers(*it);
}
mModifyMask = LLFriendObserver::NONE;
mChangedBuddyIDs.clear();
mIsNotifyObservers = false;
}
void LLAvatarTracker::addParticularFriendObserver(const LLUUID& buddy_id, LLFriendObserver* observer)
{
if (buddy_id.notNull() && observer)
mParticularFriendObserverMap[buddy_id].insert(observer);
}
void LLAvatarTracker::removeParticularFriendObserver(const LLUUID& buddy_id, LLFriendObserver* observer)
{
if (buddy_id.isNull() || !observer)
return;
observer_map_t::iterator obs_it = mParticularFriendObserverMap.find(buddy_id);
if(obs_it == mParticularFriendObserverMap.end())
return;
obs_it->second.erase(observer);
// purge empty sets from the map
// AO: Remove below check as last resort to resolve a crash from dangling pointer.
// TODO: clean up all observers and don't leave dangling pointers here.
if (obs_it->second.size() == 0)
mParticularFriendObserverMap.erase(obs_it);
}
void LLAvatarTracker::notifyParticularFriendObservers(const LLUUID& buddy_id)
{
observer_map_t::iterator obs_it = mParticularFriendObserverMap.find(buddy_id);
if(obs_it == mParticularFriendObserverMap.end())
return;
// Notify observers interested in buddy_id.
// <FS:ND> FIRE-6077; FIRE-6227; FIRE-6431; SUP-9654; make a copy of observer_set. Just in case some implementation of changed() calls add/remove...Observer
// observer_set_t& obs = obs_it->second;
observer_set_t obs = obs_it->second;
// </FS:ND:
for (observer_set_t::iterator ob_it = obs.begin(); ob_it != obs.end(); ob_it++)
{
(*ob_it)->changed(mModifyMask);
// <FS:ND/> Paranoia check. Of course someone could add x observer than add the same amount of x. That won't be found by comparing size alone, but it is good enough for a quick test
llassert( obs.size() == obs_it->second.size() );
}
}
void LLAvatarTracker::addFriendPermissionObserver(const LLUUID& buddy_id, LLFriendObserver* observer)
{
if (buddy_id.notNull() && observer)
{
mFriendPermissionObserverMap[buddy_id].insert(observer);
}
}
void LLAvatarTracker::removeFriendPermissionObserver(const LLUUID& buddy_id, LLFriendObserver* observer)
{
if (buddy_id.isNull() || !observer)
return;
observer_map_t::iterator obs_it = mFriendPermissionObserverMap.find(buddy_id);
if(obs_it == mFriendPermissionObserverMap.end())
return;
obs_it->second.erase(observer);
// purge empty sets from the map
if (obs_it->second.size() == 0)
mFriendPermissionObserverMap.erase(obs_it);
}
void LLAvatarTracker::notifyFriendPermissionObservers(const LLUUID& buddy_id)
{
observer_map_t::iterator obs_it = mFriendPermissionObserverMap.find(buddy_id);
if(obs_it == mFriendPermissionObserverMap.end())
{
return;
}
// Notify observers interested in buddy_id.
// <FS:ND> FIRE-6077; FIRE-6227; FIRE-6431; SUP-9654; make a copy of observer_set. Just in case some implementation of changed() calls add/remove...Observer
// observer_set_t& obs = obs_it->second;
observer_set_t obs = obs_it->second;
// </FS:ND>
for (observer_set_t::iterator ob_it = obs.begin(); ob_it != obs.end(); ob_it++)
{
(*ob_it)->changed(LLFriendObserver::PERMS);
// <FS:ND/> Paranoia check. Of course someone could add x observer than add the same amount of x. That won't be found by comparing size alone, but it is good enough for a quick test
llassert( obs.size() == obs_it->second.size() );
}
}
// store flag for change
// and id of object change applies to
void LLAvatarTracker::addChangedMask(U32 mask, const LLUUID& referent)
{
mModifyMask |= mask;
if (referent.notNull())
{
mChangedBuddyIDs.insert(referent);
}
}
void LLAvatarTracker::applyFunctor(LLRelationshipFunctor& f)
{
buddy_map_t::iterator it = mBuddyInfo.begin();
buddy_map_t::iterator end = mBuddyInfo.end();
for(; it != end; ++it)
{
f((*it).first, (*it).second);
}
}
void LLAvatarTracker::registerCallbacks(LLMessageSystem* msg)
{
msg->setHandlerFuncFast(_PREHASH_FindAgent, processAgentFound);
msg->setHandlerFuncFast(_PREHASH_OnlineNotification,
processOnlineNotification);
msg->setHandlerFuncFast(_PREHASH_OfflineNotification,
processOfflineNotification);
//msg->setHandlerFuncFast(_PREHASH_GrantedProxies,
// processGrantedProxies);
msg->setHandlerFunc("TerminateFriendship", processTerminateFriendship);
msg->setHandlerFunc(_PREHASH_ChangeUserRights, processChangeUserRights);
}
// static
void LLAvatarTracker::processAgentFound(LLMessageSystem* msg, void**)
{
LLUUID id;
msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_Hunter, id);
msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_Prey, id);
// *FIX: should make sure prey id matches.
LLVector3d estimated_global_pos;
msg->getF64Fast(_PREHASH_LocationBlock, _PREHASH_GlobalX,
estimated_global_pos.mdV[VX]);
msg->getF64Fast(_PREHASH_LocationBlock, _PREHASH_GlobalY,
estimated_global_pos.mdV[VY]);
LLAvatarTracker::instance().agentFound(id, estimated_global_pos);
}
void LLAvatarTracker::agentFound(const LLUUID& prey,
const LLVector3d& estimated_global_pos)
{
if(!mTrackingData) return;
//if we get a valid reply from the server, that means the agent
//is our friend and mappable, so enable interest list based updates
LLAvatarTracker::instance().setTrackedAgentValid(true);
mTrackingData->agentFound(prey, estimated_global_pos);
}
// static
void LLAvatarTracker::processOnlineNotification(LLMessageSystem* msg, void**)
{
LL_DEBUGS() << "LLAvatarTracker::processOnlineNotification()" << LL_ENDL;
instance().processNotify(msg, true);
}
// static
void LLAvatarTracker::processOfflineNotification(LLMessageSystem* msg, void**)
{
LL_DEBUGS() << "LLAvatarTracker::processOfflineNotification()" << LL_ENDL;
instance().processNotify(msg, false);
}
void LLAvatarTracker::processChange(LLMessageSystem* msg)
{
S32 count = msg->getNumberOfBlocksFast(_PREHASH_Rights);
LLUUID agent_id, agent_related;
S32 new_rights;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
for(int i = 0; i < count; ++i)
{
msg->getUUIDFast(_PREHASH_Rights, _PREHASH_AgentRelated, agent_related, i);
msg->getS32Fast(_PREHASH_Rights,_PREHASH_RelatedRights, new_rights, i);
if(agent_id == gAgent.getID())
{
if(mBuddyInfo.find(agent_related) != mBuddyInfo.end())
{
(mBuddyInfo[agent_related])->setRightsTo(new_rights);
mChangedBuddyIDs.insert(agent_related);
}
}
else
{
if(mBuddyInfo.find(agent_id) != mBuddyInfo.end())
{
if (((mBuddyInfo[agent_id]->getRightsGrantedFrom() ^ new_rights) & LLRelationship::GRANT_MODIFY_OBJECTS))
{
LLSD args;
// <FS:Ansariel> Always show complete name in rights dialogs
//args["NAME"] = LLSLURL("agent", agent_id, "displayname").getSLURLString();
args["NAME"] = LLSLURL("agent", agent_id, "completename").getSLURLString();
LLSD payload;
payload["from_id"] = agent_id;
if(LLRelationship::GRANT_MODIFY_OBJECTS & new_rights)
{
LLNotifications::instance().add("GrantedModifyRights",args, payload);
}
else
{
LLNotifications::instance().add("RevokedModifyRights",args, payload);
}
}
// <FS:Ansariel> Online status right apparently only provided as part of login response in idle_startup (response["buddy-list"]),
// so we can only keep current grant
new_rights = new_rights | (mBuddyInfo[agent_id]->getRightsGrantedFrom() & LLRelationship::GRANT_ONLINE_STATUS);
(mBuddyInfo[agent_id])->setRightsFrom(new_rights);
}
}
}
addChangedMask(LLFriendObserver::POWERS, agent_id);
notifyObservers();
notifyFriendPermissionObservers(agent_related);
}
void LLAvatarTracker::processChangeUserRights(LLMessageSystem* msg, void**)
{
LL_DEBUGS() << "LLAvatarTracker::processChangeUserRights()" << LL_ENDL;
instance().processChange(msg);
}
void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online)
{
LL_PROFILE_ZONE_SCOPED
S32 count = msg->getNumberOfBlocksFast(_PREHASH_AgentBlock);
// <FS:PP> Attempt to speed up things a little
// bool chat_notify = gSavedSettings.getBOOL("ChatOnlineNotification");
static LLCachedControl<bool> ChatOnlineNotification(gSavedSettings, "ChatOnlineNotification");
bool chat_notify = ChatOnlineNotification;
// </FS:PP>
LL_DEBUGS() << "Received " << count << " online notifications **** " << LL_ENDL;
if(count > 0)
{
LLUUID agent_id;
const LLRelationship* info = NULL;
LLUUID tracking_id;
if(mTrackingData)
{
tracking_id = mTrackingData->mAvatarID;
}
LLSD payload;
for(S32 i = 0; i < count; ++i)
{
msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_AgentID, agent_id, i);
payload["FROM_ID"] = agent_id;
info = getBuddyInfo(agent_id);
if(info)
{
setBuddyOnline(agent_id,online);
}
else
{
LL_WARNS() << "Received online notification for unknown buddy: "
<< agent_id << " is " << (online ? "ONLINE" : "OFFLINE") << LL_ENDL;
}
if(tracking_id == agent_id)
{
// we were tracking someone who went offline
deleteTrackingData();
}
//[FIX FIRE-3522 : SJ] Notify Online/Offline to Nearby Chat even if chat_notify isnt true
// <FS:PP> Attempt to speed up things a little
// if(chat_notify||LGGContactSets::getInstance()->notifyForFriend(agent_id)||gSavedSettings.getBOOL("OnlineOfflinetoNearbyChat"))
static LLCachedControl<bool> OnlineOfflinetoNearbyChat(gSavedSettings, "OnlineOfflinetoNearbyChat");
if(chat_notify || LGGContactSets::getInstance()->notifyForFriend(agent_id) || OnlineOfflinetoNearbyChat)
// </FS:PP>
{
// Look up the name of this agent for the notification
LLAvatarNameCache::get(agent_id,boost::bind(&on_avatar_name_cache_notify,_1, _2, online, payload));
}
}
mModifyMask |= LLFriendObserver::ONLINE;
instance().notifyObservers();
gInventory.notifyObservers();
}
}
static void on_avatar_name_cache_notify(const LLUUID& agent_id,
const LLAvatarName& av_name,
bool online,
LLSD payload)
{
// Popup a notify box with online status of this agent
// Use display name only because this user is your friend
LLSD args;
// <FS:Ansariel> Make name clickable
// args["NAME"] = av_name.getDisplayName();
std::string used_name = FSCommon::getAvatarNameByDisplaySettings(av_name);
args["NAME"] = used_name;
// </FS:Ansariel>
args["STATUS"] = online ? LLTrans::getString("OnlineStatus") : LLTrans::getString("OfflineStatus");
args["AGENT-ID"] = agent_id;
LLNotificationPtr notification;
if (online)
{
make_ui_sound("UISndFriendOnline"); // <FS:PP> FIRE-2731: Online/offline sound alert for friends
notification =
LLNotifications::instance().add("FriendOnlineOffline",
args,
payload.with("respond_on_mousedown", true),
boost::bind(&LLAvatarActions::startIM, agent_id));
}
else
{
make_ui_sound("UISndFriendOffline"); // <FS:PP> FIRE-2731: Online/offline sound alert for friends
notification =
LLNotifications::instance().add("FriendOnlineOffline", args, payload);
}
// If there's an open IM session with this agent, send a notification there too.
LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, agent_id);
std::string notify_msg = notification->getMessage();
LLIMModel::instance().proccessOnlineOfflineNotification(session_id, notify_msg);
// If desired, also send it to nearby chat, this allows friends'
// online/offline times to be referenced in chat & logged.
// [FIRE-3522 : SJ] Only show Online/Offline toast for groups which have enabled "Show notice for this set" and in the settingpage of CS is checked that the messages need to be in Toasts
// or for groups which have enabled "Show notice for this set" and in the settingpage of CS is checked that the messages need to be in Nearby Chat
static LLCachedControl<bool> OnlineOfflinetoNearbyChat(gSavedSettings, "OnlineOfflinetoNearbyChat");
static LLCachedControl<bool> FSContactSetsNotificationNearbyChat(gSavedSettings, "FSContactSetsNotificationNearbyChat");
if ((OnlineOfflinetoNearbyChat) || (FSContactSetsNotificationNearbyChat && LGGContactSets::getInstance()->notifyForFriend(agent_id)))
{
static LLCachedControl<bool> history_only(gSavedSettings, "OnlineOfflinetoNearbyChatHistory"); // LO - Adding a setting to show online/offline notices only in chat history. Helps prevent your screen from being filled with online notices on login.
LLChat chat;
chat.mText = (online ? LLTrans::getString("FriendOnlineNotification") : LLTrans::getString("FriendOfflineNotification"));
chat.mSourceType = CHAT_SOURCE_SYSTEM;
chat.mChatType = CHAT_TYPE_RADAR;
chat.mFromID = agent_id;
chat.mFromName = used_name;
if (history_only)
{
FSFloaterNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<FSFloaterNearbyChat>("fs_nearby_chat", LLSD());
nearby_chat->addMessage(chat, true, LLSD());
}
else
{
args["ONLINE_STATUS"] = true;
LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args);
}
// <FS:PP> FIRE-10178: Keyword Alerts in group IM do not work unless the group is in the foreground (notification on receipt of IM)
chat.mText = notify_msg;
if (FSKeywords::getInstance()->chatContainsKeyword(chat, true))
{
FSKeywords::notify(chat);
}
// </FS:PP>
}
}
void LLAvatarTracker::formFriendship(const LLUUID& id)
{
if(id.notNull())
{
LLRelationship* buddy_info = get_ptr_in_map(instance().mBuddyInfo, id);
if(!buddy_info)
{
LLAvatarTracker& at = LLAvatarTracker::instance();
//The default for relationship establishment is to have both parties
//visible online to each other.
buddy_info = new LLRelationship(LLRelationship::GRANT_ONLINE_STATUS,LLRelationship::GRANT_ONLINE_STATUS, false);
at.mBuddyInfo[id] = buddy_info;
at.addChangedMask(LLFriendObserver::ADD, id);
at.notifyObservers();
}
}
}
void LLAvatarTracker::processTerminateFriendship(LLMessageSystem* msg, void**)
{
LLUUID id;
msg->getUUID("ExBlock", "OtherID", id);
if(id.notNull())
{
LLAvatarTracker& at = LLAvatarTracker::instance();
LLRelationship* buddy = get_ptr_in_map(at.mBuddyInfo, id);
if(!buddy) return;
at.mBuddyInfo.erase(id);
at.addChangedMask(LLFriendObserver::REMOVE, id);
delete buddy;
at.notifyObservers();
}
}
///----------------------------------------------------------------------------
/// Tracking Data
///----------------------------------------------------------------------------
LLTrackingData::LLTrackingData(const LLUUID& avatar_id, const std::string& name)
: mAvatarID(avatar_id),
mHaveInfo(false),
mHaveCoarseInfo(false)
{
mCoarseLocationTimer.setTimerExpirySec(COARSE_FREQUENCY);
mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY);
mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
if(!name.empty())
{
mName = name;
}
}
void LLTrackingData::agentFound(const LLUUID& prey,
const LLVector3d& estimated_global_pos)
{
if(prey != mAvatarID)
{
LL_WARNS() << "LLTrackingData::agentFound() - found " << prey
<< " but looking for " << mAvatarID << LL_ENDL;
}
mHaveInfo = true;
mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
mGlobalPositionEstimate = estimated_global_pos;
}
bool LLTrackingData::haveTrackingInfo()
{
LLViewerObject* object = gObjectList.findObject(mAvatarID);
if(object && !object->isDead())
{
mCoarseLocationTimer.checkExpirationAndReset(COARSE_FREQUENCY);
mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY);
mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
mHaveInfo = true;
return true;
}
if(mHaveCoarseInfo &&
!mCoarseLocationTimer.checkExpirationAndReset(COARSE_FREQUENCY))
{
// if we reach here, then we have a 'recent' coarse update
mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY);
mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
return true;
}
if(mUpdateTimer.checkExpirationAndReset(FIND_FREQUENCY))
{
LLAvatarTracker::instance().findAgent();
mHaveCoarseInfo = false;
}
if(mAgentGone.checkExpirationAndReset(OFFLINE_SECONDS))
{
mHaveInfo = false;
mHaveCoarseInfo = false;
}
return mHaveInfo;
}
void LLTrackingData::setTrackedCoarseLocation(const LLVector3d& global_pos)
{
mCoarseLocationTimer.setTimerExpirySec(COARSE_FREQUENCY);
mGlobalPositionEstimate = global_pos;
mHaveInfo = true;
mHaveCoarseInfo = true;
}
///----------------------------------------------------------------------------
// various buddy functors
///----------------------------------------------------------------------------
bool LLCollectProxyBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
{
if(buddy->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS))
{
mProxy.insert(buddy_id);
}
return true;
}
bool LLCollectMappableBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
{
LLAvatarName av_name;
LLAvatarNameCache::get( buddy_id, &av_name);
buddy_map_t::value_type value(buddy_id, av_name.getDisplayName());
if(buddy->isOnline() && buddy->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION))
{
// <FS:Ansariel> Friend names on worldmap should respect display name settings
//mMappable.insert(value);
// <FS:PP> Attempt to speed up things a little
// if (LLAvatarNameCache::useDisplayNames() && gSavedSettings.getBOOL("NameTagShowUsernames"))
static LLCachedControl<bool> NameTagShowUsernames(gSavedSettings, "NameTagShowUsernames");
if (LLAvatarName::useDisplayNames() && NameTagShowUsernames)
// </FS:PP>
{
buddy_map_t::value_type value(buddy_id, av_name.getCompleteName());
mMappable.insert(value);
}
else
{
buddy_map_t::value_type value(buddy_id, av_name.getDisplayName());
mMappable.insert(value);
}
// </FS:Ansariel>
}
return true;
}
bool LLCollectOnlineBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
{
LLAvatarName av_name;
LLAvatarNameCache::get(buddy_id, &av_name);
mFullName = av_name.getUserName();
buddy_map_t::value_type value(buddy_id, mFullName);
if(buddy->isOnline())
{
mOnline.insert(value);
}
return true;
}
bool LLCollectAllBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
{
LLAvatarName av_name;
LLAvatarNameCache::get(buddy_id, &av_name);
// <FS:Ansariel> FIRE-13756: Friends avatar picker only shows display name
//mFullName = av_name.getDisplayName();
mFullName = FSCommon::getAvatarNameByDisplaySettings(av_name);
// </FS:Ansariel>
buddy_map_t::value_type value(buddy_id, mFullName);
if(buddy->isOnline())
{
mOnline.insert(value);
}
else
{
mOffline.insert(value);
}
return true;
}