-
James Cook authored
Also fixes a crash when turning on display names.
James Cook authoredAlso fixes a crash when turning on display names.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
llavatarnamecache.cpp 7.86 KiB
/**
* @file llavatarnamecache.cpp
* @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names
* ("James Cook") from avatar UUIDs.
*
* $LicenseInfo:firstyear=2010&license=viewergpl$
*
* Copyright (c) 2010, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llavatarnamecache.h"
#include "llcachename.h" // *TODO: remove
#include "llframetimer.h"
#include "llhttpclient.h"
#include <map>
#include <set>
namespace LLAvatarNameCache
{
bool sUseDisplayNames = false;
// *TODO: configure the base URL for this in viewer with data
// from login.cgi
std::string sNameServiceBaseURL = "http://pdp15.lindenlab.com:8050/my-service/";
// accumulated agent IDs for next query against service
typedef std::set<LLUUID> ask_queue_t;
ask_queue_t sAskQueue;
// agent IDs that have been requested, but with no reply
// maps agent ID to frame time request was made
typedef std::map<LLUUID, F32> pending_queue_t;
pending_queue_t sPendingQueue;
// Callbacks to fire when we received a name.
// May have multiple callbacks for a single ID, which are
// represented as multiple slots bound to the signal.
// Avoid copying signals via pointers.
typedef std::map<LLUUID, callback_signal_t*> signal_map_t;
signal_map_t sSignalMap;
// names we know about
typedef std::map<LLUUID, LLAvatarName> cache_t;
cache_t sCache;
// only need per-frame timing resolution
LLFrameTimer sRequestTimer;
bool isRequestPending(const LLUUID& agent_id);
void processNameFromService(const LLSD& row);
}
class LLAvatarNameResponder : public LLHTTPClient::Responder
{
public:
/*virtual*/ void result(const LLSD& content)
{
LLSD::array_const_iterator it = content.beginArray();
for ( ; it != content.endArray(); ++it)
{
const LLSD& row = *it;
LLAvatarNameCache::processNameFromService(row);
}
}
/*virtual*/ void error(U32 status, const std::string& reason)
{
llinfos << "JAMESDEBUG error " << status << " " << reason << llendl;
}
};
void LLAvatarNameCache::processNameFromService(const LLSD& row)
{
U32 now = (U32)LLFrameTimer::getTotalSeconds();
LLAvatarName av_name;
av_name.mSLID = row["slid"].asString();
av_name.mDisplayName = row["display_name"].asString();
av_name.mLastUpdate = now;
// HACK for pretty stars
if (row["last_name"].asString() == "Linden")
{
av_name.mBadge = "Person_Star";
}
// Some avatars don't have explicit display names set
if (av_name.mDisplayName.empty())
{
// make up a display name
std::string first_name = row["first_name"].asString();
std::string last_name = row["last_name"].asString();
av_name.mDisplayName =
LLCacheName::buildFullName(first_name, last_name);
av_name.mIsLegacy = true;
}
// add to cache
LLUUID agent_id = row["agent_id"].asUUID();
sCache[agent_id] = av_name;
sPendingQueue.erase(agent_id);
// signal everyone waiting on this name
signal_map_t::iterator sig_it = sSignalMap.find(agent_id);
if (sig_it != sSignalMap.end())
{
callback_signal_t* signal = sig_it->second;
(*signal)(agent_id, av_name);
sSignalMap.erase(agent_id);
delete signal;
signal = NULL;
}
}
void LLAvatarNameCache::initClass()
{
}
void LLAvatarNameCache::cleanupClass()
{
}
void LLAvatarNameCache::importFile(std::istream& istr)
{
}
void LLAvatarNameCache::exportFile(std::ostream& ostr)
{
}
void LLAvatarNameCache::idle()
{
const F32 SECS_BETWEEN_REQUESTS = 0.2f; // JAMESDEBUG set to 0.1?
if (sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS))
{
return;
}
if (sAskQueue.empty())
{
return;
}
LLSD body;
body["agent_ids"] = LLSD::emptyArray();
LLSD& agent_ids = body["agent_ids"];
ask_queue_t::const_iterator it = sAskQueue.begin();
for ( ; it != sAskQueue.end(); ++it)
{
agent_ids.append( LLSD( *it ) );
}
// *TODO: configure the base URL for this
std::string url = sNameServiceBaseURL + "agent/display-names/";
LLHTTPClient::post(url, body, new LLAvatarNameResponder());
// Move requests from Ask queue to Pending queue
U32 now = (U32)LLFrameTimer::getTotalSeconds();
for (it = sAskQueue.begin(); it != sAskQueue.end(); ++it)
{
sPendingQueue[*it] = now;
}
sAskQueue.clear();
}
bool LLAvatarNameCache::isRequestPending(const LLUUID& agent_id)
{
const U32 PENDING_TIMEOUT_SECS = 5 * 60;
U32 now = (U32)LLFrameTimer::getTotalSeconds();
U32 expire_time = now - PENDING_TIMEOUT_SECS;
pending_queue_t::const_iterator it = sPendingQueue.find(agent_id);
if (it != sPendingQueue.end())
{
bool expired = (it->second < expire_time);
return !expired;
}
return false;
}
bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
{
std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id);
if (it != sCache.end())
{
*av_name = it->second;
return true;
}
if (!isRequestPending(agent_id))
{
sAskQueue.insert(agent_id);
}
return false;
}
void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot)
{
std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id);
if (it != sCache.end())
{
// ...name already exists in cache, fire callback now
callback_signal_t signal;
signal.connect(slot);
signal(agent_id, it->second);
return;
}
// schedule a request
if (!isRequestPending(agent_id))
{
sAskQueue.insert(agent_id);
}
// always store additional callback, even if request is pending
signal_map_t::iterator sig_it = sSignalMap.find(agent_id);
if (sig_it == sSignalMap.end())
{
// ...new callback for this id
callback_signal_t* signal = new callback_signal_t();
signal->connect(slot);
sSignalMap[agent_id] = signal;
}
else
{
// ...existing callback, bind additional slot
callback_signal_t* signal = sig_it->second;
signal->connect(slot);
}
}
class LLSetNameResponder : public LLHTTPClient::Responder
{
public:
LLUUID mAgentID;
LLSetNameResponder(const LLUUID& agent_id) : mAgentID(agent_id) { }
/*virtual*/ void result(const LLSD& content)
{
// force re-fetch
LLAvatarNameCache::sCache.erase(mAgentID);
}
/*virtual*/ void error(U32 status, const std::string& reason)
{
llinfos << "JAMESDEBUG set names failed " << status
<< " reason " << reason << llendl;
}
};
void LLAvatarNameCache::setDisplayName(const LLUUID& agent_id, const std::string& display_name)
{
LLSD body;
body["display_name"] = display_name;
// *TODO: configure the base URL for this
std::string url = sNameServiceBaseURL + "agent/";
url += agent_id.asString();
url += "/set-display-name/";
LLHTTPClient::post(url, body, new LLSetNameResponder(agent_id));
}
void LLAvatarNameCache::toggleDisplayNames()
{
sUseDisplayNames = !sUseDisplayNames;
// flush our cache
sCache.clear();
// force re-lookups
if (gCacheName)
{
gCacheName->clear();
}
}
bool LLAvatarNameCache::useDisplayNames()
{
return sUseDisplayNames;
}
void LLAvatarNameCache::erase(const LLUUID& agent_id)
{
sCache.erase(agent_id);
}