Shiny new name cache.
This commit is contained in:
@@ -25,10 +25,6 @@
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
/*
|
||||
Ported to Phoenix by Wolfspirit Magic.
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "llavatarnamecache.h"
|
||||
@@ -91,8 +87,11 @@ namespace LLAvatarNameCache
|
||||
// only need per-frame timing resolution
|
||||
LLFrameTimer sRequestTimer;
|
||||
|
||||
// Periodically clean out expired entries from the cache
|
||||
//LLFrameTimer sEraseExpiredTimer;
|
||||
/// Maximum time an unrefreshed cache entry is allowed
|
||||
const F64 MAX_UNREFRESHED_TIME = 20.0 * 60.0;
|
||||
|
||||
/// Time when unrefreshed cached names were checked last
|
||||
static F64 sLastExpireCheck;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Internal methods
|
||||
@@ -107,10 +106,11 @@ namespace LLAvatarNameCache
|
||||
|
||||
void requestNamesViaCapability();
|
||||
|
||||
|
||||
// agent_id/group_id, first_name, last_name, is_group, user_data
|
||||
// Legacy name system callback
|
||||
void legacyNameCallback(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data);
|
||||
void legacyNameCallback(const LLUUID& agent_id,
|
||||
const std::string& full_name,
|
||||
bool is_group
|
||||
);
|
||||
|
||||
void requestNamesViaLegacy();
|
||||
|
||||
@@ -124,9 +124,10 @@ namespace LLAvatarNameCache
|
||||
const LLAvatarName& av_name);
|
||||
|
||||
// Is a request in-flight over the network?
|
||||
bool isRequestPending(const LLUUID& agent_id);
|
||||
|
||||
// Erase expired names from cache
|
||||
void eraseExpired();
|
||||
void eraseUnrefreshed();
|
||||
|
||||
bool expirationFromCacheControl(LLSD headers, F64 *expires);
|
||||
}
|
||||
@@ -196,6 +197,7 @@ public:
|
||||
{
|
||||
// Pull expiration out of headers if available
|
||||
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(mHeaders);
|
||||
F64 now = LLFrameTimer::getTotalSeconds();
|
||||
|
||||
LLSD agents = content["agents"];
|
||||
LLSD::array_const_iterator it = agents.beginArray();
|
||||
@@ -216,118 +218,91 @@ public:
|
||||
av_name.mDisplayName = av_name.mUsername;
|
||||
}
|
||||
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << " "
|
||||
<< "user '" << av_name.mUsername << "' "
|
||||
<< "display '" << av_name.mDisplayName << "' "
|
||||
<< "expires in " << expires - now << " seconds"
|
||||
<< LL_ENDL;
|
||||
|
||||
// cache it and fire signals
|
||||
LLAvatarNameCache::processName(agent_id, av_name, true);
|
||||
}
|
||||
|
||||
// Same logic as error response case
|
||||
LLSD unresolved_agents = content["bad_ids"];
|
||||
if (unresolved_agents.size() > 0)
|
||||
S32 num_unresolved = unresolved_agents.size();
|
||||
if (num_unresolved > 0)
|
||||
{
|
||||
const std::string DUMMY_NAME("\?\?\?");
|
||||
LLAvatarName av_name;
|
||||
av_name.mUsername = DUMMY_NAME;
|
||||
av_name.mDisplayName = DUMMY_NAME;
|
||||
av_name.mIsDisplayNameDefault = false;
|
||||
av_name.mIsDummy = true;
|
||||
av_name.mExpires = expires;
|
||||
|
||||
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result " << num_unresolved << " unresolved ids; "
|
||||
<< "expires in " << expires - now << " seconds"
|
||||
<< LL_ENDL;
|
||||
it = unresolved_agents.beginArray();
|
||||
for ( ; it != unresolved_agents.endArray(); ++it)
|
||||
{
|
||||
const LLUUID& agent_id = *it;
|
||||
// cache it and fire signals
|
||||
|
||||
// Wolfspirit: Do not use ??? as username. Try to get a username out of the old legacy name
|
||||
std::string oldname;
|
||||
gCacheName->getFullName(agent_id, oldname);
|
||||
LLStringUtil::toLower(oldname);
|
||||
LLStringUtil::replaceString(oldname," ",".");
|
||||
LLStringUtil::replaceString(oldname,".resident","");
|
||||
av_name.mUsername = oldname;
|
||||
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::result "
|
||||
<< "failed id " << agent_id
|
||||
<< LL_ENDL;
|
||||
|
||||
LLAvatarNameCache::processName(agent_id, av_name, true);
|
||||
LLAvatarNameCache::handleAgentError(agent_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result "
|
||||
<< LLAvatarNameCache::sCache.size() << " cached names"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
/*virtual*/ void error(U32 status, const std::string& reason)
|
||||
{
|
||||
// We're going to construct a dummy record and cache it for a while,
|
||||
// either briefly for a 503 Service Unavailable, or longer for other
|
||||
// errors.
|
||||
F64 retry_timestamp = errorRetryTimestamp(status);
|
||||
// If there's an error, it might be caused by PeopleApi,
|
||||
// or when loading textures on startup and using a very slow
|
||||
// network, this query may time out.
|
||||
// What we should do depends on whether or not we have a cached name
|
||||
LL_WARNS("AvNameCache") << "LLAvatarNameResponder::error " << status << " " << reason
|
||||
<< LL_ENDL;
|
||||
|
||||
// *NOTE: "??" starts trigraphs in C/C++, escape the question marks.
|
||||
const std::string DUMMY_NAME("\?\?\?");
|
||||
LLAvatarName av_name;
|
||||
av_name.mUsername = DUMMY_NAME;
|
||||
av_name.mDisplayName = DUMMY_NAME;
|
||||
av_name.mIsDisplayNameDefault = false;
|
||||
av_name.mIsDummy = true;
|
||||
av_name.mExpires = retry_timestamp;
|
||||
|
||||
// Add dummy records for all agent IDs in this request
|
||||
// Add dummy records for any agent IDs in this request that we do not have cached already
|
||||
std::vector<LLUUID>::const_iterator it = mAgentIDs.begin();
|
||||
for ( ; it != mAgentIDs.end(); ++it)
|
||||
{
|
||||
const LLUUID& agent_id = *it;
|
||||
// cache it and fire signals
|
||||
|
||||
// Wolfspirit: Do not use ??? as username. Try to get a username out of the old legacy name
|
||||
std::string oldname;
|
||||
gCacheName->getFullName(agent_id, oldname);
|
||||
LLStringUtil::toLower(oldname);
|
||||
LLStringUtil::replaceString(oldname," ",".");
|
||||
LLStringUtil::replaceString(oldname,".resident","");
|
||||
av_name.mUsername = oldname;
|
||||
|
||||
LLAvatarNameCache::processName(agent_id, av_name, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Return time to retry a request that generated an error, based on
|
||||
// error type and headers. Return value is seconds-since-epoch.
|
||||
F64 errorRetryTimestamp(S32 status)
|
||||
{
|
||||
F64 now = LLFrameTimer::getTotalSeconds();
|
||||
|
||||
// Retry-After takes priority
|
||||
LLSD retry_after = mHeaders["retry-after"];
|
||||
if (retry_after.isDefined())
|
||||
{
|
||||
// We only support the delta-seconds type
|
||||
S32 delta_seconds = retry_after.asInteger();
|
||||
if (delta_seconds > 0)
|
||||
{
|
||||
// ...valid delta-seconds
|
||||
return now + F64(delta_seconds);
|
||||
}
|
||||
}
|
||||
|
||||
// If no Retry-After, look for Cache-Control max-age
|
||||
F64 expires = 0.0;
|
||||
if (LLAvatarNameCache::expirationFromCacheControl(mHeaders, &expires))
|
||||
{
|
||||
return expires;
|
||||
}
|
||||
|
||||
// No information in header, make a guess
|
||||
if (status == 503)
|
||||
{
|
||||
// ...service unavailable, retry soon
|
||||
const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min
|
||||
return now + SERVICE_UNAVAILABLE_DELAY;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...other unexpected error
|
||||
const F64 DEFAULT_DELAY = 3600.0; // 1 hour
|
||||
return now + DEFAULT_DELAY;
|
||||
LLAvatarNameCache::handleAgentError(agent_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Provide some fallback for agents that return errors
|
||||
void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id)
|
||||
{
|
||||
std::map<LLUUID,LLAvatarName>::iterator existing = sCache.find(agent_id);
|
||||
if (existing == sCache.end())
|
||||
{
|
||||
// there is no existing cache entry, so make a temporary name from legacy
|
||||
LL_WARNS("AvNameCache") << "LLAvatarNameCache get legacy for agent "
|
||||
<< agent_id << LL_ENDL;
|
||||
gCacheName->get(agent_id, false, // legacy compatibility
|
||||
boost::bind(&LLAvatarNameCache::legacyNameCallback,
|
||||
_1, _2, _3));
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have a chached (but probably expired) entry - since that would have
|
||||
// been returned by the get method, there is no need to signal anyone
|
||||
|
||||
// Clear this agent from the pending list
|
||||
LLAvatarNameCache::sPendingQueue.erase(agent_id);
|
||||
|
||||
const LLAvatarName& av_name = existing->second;
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameCache use cache for agent "
|
||||
<< agent_id
|
||||
<< "user '" << av_name.mUsername << "' "
|
||||
<< "display '" << av_name.mDisplayName << "' "
|
||||
<< "expires in " << av_name.mExpires - LLFrameTimer::getTotalSeconds() << " seconds"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLAvatarNameCache::processName(const LLUUID& agent_id,
|
||||
const LLAvatarName& av_name,
|
||||
bool add_to_cache)
|
||||
@@ -336,12 +311,12 @@ void LLAvatarNameCache::processName(const LLUUID& agent_id,
|
||||
{
|
||||
// sCache[agent_id] = av_name;
|
||||
// [SL:KB] - Patch: Agent-DisplayNames | Checked: 2010-12-28 (Catznip-2.4.0h) | Added: Catznip-2.4.0h
|
||||
// Don't replace existing entries with dummies
|
||||
cache_t::iterator itName = (av_name.mIsDummy) ? sCache.find(agent_id) : sCache.end();
|
||||
if (sCache.end() != itName)
|
||||
itName->second.mExpires = av_name.mExpires;
|
||||
else
|
||||
sCache[agent_id] = av_name;
|
||||
// Don't replace existing entries with dummies
|
||||
cache_t::iterator itName = (av_name.mIsTemporaryName) ? sCache.find(agent_id) : sCache.end();
|
||||
if (sCache.end() != itName)
|
||||
itName->second.mExpires = av_name.mExpires;
|
||||
else
|
||||
sCache[agent_id] = av_name;
|
||||
// [/SL:KB]
|
||||
}
|
||||
|
||||
@@ -377,6 +352,7 @@ void LLAvatarNameCache::requestNamesViaCapability()
|
||||
std::vector<LLUUID> agent_ids;
|
||||
agent_ids.reserve(128);
|
||||
|
||||
U32 ids = 0;
|
||||
ask_queue_t::const_iterator it = sAskQueue.begin();
|
||||
for ( ; it != sAskQueue.end(); ++it)
|
||||
{
|
||||
@@ -387,11 +363,13 @@ void LLAvatarNameCache::requestNamesViaCapability()
|
||||
// ...starting new request
|
||||
url += sNameLookupURL;
|
||||
url += "?ids=";
|
||||
ids = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...continuing existing request
|
||||
url += "&ids=";
|
||||
ids++;
|
||||
}
|
||||
url += agent_id.asString();
|
||||
agent_ids.push_back(agent_id);
|
||||
@@ -401,7 +379,9 @@ void LLAvatarNameCache::requestNamesViaCapability()
|
||||
|
||||
if (url.size() > NAME_URL_SEND_THRESHOLD)
|
||||
{
|
||||
//llinfos << "requestNames " << url << llendl;
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability first "
|
||||
<< ids << " ids"
|
||||
<< LL_ENDL;
|
||||
LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids));
|
||||
url.clear();
|
||||
agent_ids.clear();
|
||||
@@ -410,7 +390,9 @@ void LLAvatarNameCache::requestNamesViaCapability()
|
||||
|
||||
if (!url.empty())
|
||||
{
|
||||
//llinfos << "requestNames " << url << llendl;
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability all "
|
||||
<< ids << " ids"
|
||||
<< LL_ENDL;
|
||||
LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids));
|
||||
url.clear();
|
||||
agent_ids.clear();
|
||||
@@ -419,18 +401,25 @@ void LLAvatarNameCache::requestNamesViaCapability()
|
||||
// We've moved all asks to the pending request queue
|
||||
sAskQueue.clear();
|
||||
}
|
||||
void LLAvatarNameCache::legacyNameCallback(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data)
|
||||
|
||||
void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id,
|
||||
const std::string& full_name,
|
||||
bool is_group)
|
||||
{
|
||||
std::string full_name = first + " " +last;
|
||||
// Construct a dummy record for this name. By convention, SLID is blank
|
||||
// Never expires, but not written to disk, so lasts until end of session.
|
||||
LLAvatarName av_name;
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::legacyNameCallback "
|
||||
<< "agent " << agent_id << " "
|
||||
<< "full name '" << full_name << "'"
|
||||
<< ( is_group ? " [group]" : "" )
|
||||
<< LL_ENDL;
|
||||
buildLegacyName(full_name, &av_name);
|
||||
|
||||
// Don't add to cache, the data already exists in the legacy name system
|
||||
// cache and we don't want or need duplicate storage, because keeping the
|
||||
// two copies in sync is complex.
|
||||
processName(id, av_name, false);
|
||||
processName(agent_id, av_name, false);
|
||||
}
|
||||
|
||||
void LLAvatarNameCache::requestNamesViaLegacy()
|
||||
@@ -446,7 +435,11 @@ void LLAvatarNameCache::requestNamesViaLegacy()
|
||||
// invoked below. This should never happen in practice.
|
||||
sPendingQueue[agent_id] = now;
|
||||
|
||||
gCacheName->get(agent_id, false, legacyNameCallback);
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaLegacy agent " << agent_id << LL_ENDL;
|
||||
|
||||
gCacheName->get(agent_id, false, // legacy compatibility
|
||||
boost::bind(&LLAvatarNameCache::legacyNameCallback,
|
||||
_1, _2, _3));
|
||||
}
|
||||
|
||||
// We've either answered immediately or moved all asks to the
|
||||
@@ -482,21 +475,24 @@ void LLAvatarNameCache::importFile(std::istream& istr)
|
||||
av_name.fromLLSD( it->second );
|
||||
sCache[agent_id] = av_name;
|
||||
}
|
||||
// entries may have expired since we last ran the viewer, just
|
||||
// clean them out now
|
||||
eraseExpired();
|
||||
llinfos << "loaded " << sCache.size() << llendl;
|
||||
LL_INFOS("AvNameCache") << "loaded " << sCache.size() << LL_ENDL;
|
||||
|
||||
// Some entries may have expired since the cache was stored,
|
||||
// but they will be flushed in the first call to eraseUnrefreshed
|
||||
// from LLAvatarNameResponder::idle
|
||||
}
|
||||
|
||||
void LLAvatarNameCache::exportFile(std::ostream& ostr)
|
||||
{
|
||||
LLSD agents;
|
||||
F64 max_unrefreshed = LLFrameTimer::getTotalSeconds() - MAX_UNREFRESHED_TIME;
|
||||
cache_t::const_iterator it = sCache.begin();
|
||||
for ( ; it != sCache.end(); ++it)
|
||||
{
|
||||
const LLUUID& agent_id = it->first;
|
||||
const LLAvatarName& av_name = it->second;
|
||||
if (!av_name.mIsDummy)
|
||||
// Do not write temporary or expired entries to the stored cache
|
||||
if (!av_name.mIsTemporaryName && av_name.mExpires >= max_unrefreshed)
|
||||
{
|
||||
// key must be a string
|
||||
agents[agent_id.asString()] = av_name.asLLSD();
|
||||
@@ -531,36 +527,26 @@ void LLAvatarNameCache::idle()
|
||||
// return;
|
||||
//}
|
||||
|
||||
// Must be large relative to above
|
||||
|
||||
// No longer deleting expired entries, just re-requesting in the get
|
||||
// this way first synchronous get call on an expired entry won't return
|
||||
// legacy name. LF
|
||||
|
||||
//const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds
|
||||
//if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT))
|
||||
//{
|
||||
// eraseExpired();
|
||||
//}
|
||||
|
||||
if (sAskQueue.empty())
|
||||
if (!sAskQueue.empty())
|
||||
{
|
||||
return;
|
||||
if (useDisplayNames())
|
||||
{
|
||||
requestNamesViaCapability();
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...fall back to legacy name cache system
|
||||
requestNamesViaLegacy();
|
||||
}
|
||||
}
|
||||
|
||||
if (useDisplayNames())
|
||||
{
|
||||
requestNamesViaCapability();
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...fall back to legacy name cache system
|
||||
requestNamesViaLegacy();
|
||||
}
|
||||
// erase anything that has not been refreshed for more than MAX_UNREFRESHED_TIME
|
||||
eraseUnrefreshed();
|
||||
}
|
||||
|
||||
bool LLAvatarNameCache::isRequestPending(const LLUUID& agent_id)
|
||||
{
|
||||
bool isPending = false;
|
||||
const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0;
|
||||
|
||||
pending_queue_t::const_iterator it = sPendingQueue.find(agent_id);
|
||||
@@ -568,26 +554,38 @@ bool LLAvatarNameCache::isRequestPending(const LLUUID& agent_id)
|
||||
{
|
||||
// in the list of requests in flight, retry if too old
|
||||
F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS;
|
||||
return it->second > expire_time;
|
||||
isPending = (it->second > expire_time);
|
||||
}
|
||||
return false;
|
||||
return isPending;
|
||||
}
|
||||
|
||||
void LLAvatarNameCache::eraseExpired()
|
||||
void LLAvatarNameCache::eraseUnrefreshed()
|
||||
{
|
||||
F64 now = LLFrameTimer::getTotalSeconds();
|
||||
cache_t::iterator it = sCache.begin();
|
||||
for (cache_t::iterator it = sCache.begin(); it != sCache.end();)
|
||||
{
|
||||
const LLAvatarName& av_name = it->second;
|
||||
if (av_name.mExpires < now)
|
||||
{
|
||||
sCache.erase(it++);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
F64 max_unrefreshed = now - MAX_UNREFRESHED_TIME;
|
||||
|
||||
if (!sLastExpireCheck || sLastExpireCheck < max_unrefreshed)
|
||||
{
|
||||
sLastExpireCheck = now;
|
||||
|
||||
for (cache_t::iterator it = sCache.begin(); it != sCache.end();)
|
||||
{
|
||||
const LLAvatarName& av_name = it->second;
|
||||
if (av_name.mExpires < max_unrefreshed)
|
||||
{
|
||||
const LLUUID& agent_id = it->first;
|
||||
LL_DEBUGS("AvNameCache") << agent_id
|
||||
<< " user '" << av_name.mUsername << "' "
|
||||
<< "expired " << now - av_name.mExpires << " secs ago"
|
||||
<< LL_ENDL;
|
||||
sCache.erase(it++);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
LL_INFOS("AvNameCache") << sCache.size() << " cached avatar names" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,8 +596,11 @@ void LLAvatarNameCache::buildLegacyName(const std::string& full_name,
|
||||
av_name->mUsername = "";
|
||||
av_name->mDisplayName = full_name;
|
||||
av_name->mIsDisplayNameDefault = true;
|
||||
av_name->mIsDummy = true;
|
||||
av_name->mExpires = F64_MAX;
|
||||
av_name->mIsTemporaryName = true;
|
||||
av_name->mExpires = F64_MAX; // not used because these are not cached
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::buildLegacyName "
|
||||
<< full_name
|
||||
<< LL_ENDL;
|
||||
|
||||
// [Ansariel/Henri]
|
||||
// Why ain't those set? In case of disabled display names
|
||||
@@ -632,6 +633,9 @@ bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
|
||||
{
|
||||
if (!isRequestPending(agent_id))
|
||||
{
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::get "
|
||||
<< "refresh agent " << agent_id
|
||||
<< LL_ENDL;
|
||||
sAskQueue.insert(agent_id);
|
||||
}
|
||||
}
|
||||
@@ -641,7 +645,6 @@ bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// ...use legacy names cache
|
||||
std::string full_name;
|
||||
if (gCacheName->getFullName(agent_id, full_name))
|
||||
@@ -654,6 +657,9 @@ bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
|
||||
|
||||
if (!isRequestPending(agent_id))
|
||||
{
|
||||
LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::get "
|
||||
<< "queue request for agent " << agent_id
|
||||
<< LL_ENDL;
|
||||
sAskQueue.insert(agent_id);
|
||||
}
|
||||
|
||||
@@ -800,6 +806,8 @@ F64 LLAvatarNameCache::nameExpirationFromHeaders(LLSD headers)
|
||||
|
||||
bool LLAvatarNameCache::expirationFromCacheControl(LLSD headers, F64 *expires)
|
||||
{
|
||||
bool fromCacheControl = false;
|
||||
F64 now = LLFrameTimer::getTotalSeconds();
|
||||
// Allow the header to override the default
|
||||
LLSD cache_control_header = headers["cache-control"];
|
||||
if (cache_control_header.isDefined())
|
||||
@@ -808,12 +816,16 @@ bool LLAvatarNameCache::expirationFromCacheControl(LLSD headers, F64 *expires)
|
||||
std::string cache_control = cache_control_header.asString();
|
||||
if (max_age_from_cache_control(cache_control, &max_age))
|
||||
{
|
||||
F64 now = LLFrameTimer::getTotalSeconds();
|
||||
*expires = now + (F64)max_age;
|
||||
return true;
|
||||
fromCacheControl = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
LL_DEBUGS("AvNameCache")
|
||||
<< ( fromCacheControl ? "expires based on cache control " : "default expiration " )
|
||||
<< "in " << *expires - now << " seconds"
|
||||
<< LL_ENDL;
|
||||
|
||||
return fromCacheControl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -85,10 +85,11 @@ namespace LLAvatarNameCache
|
||||
void setForceDisplayNames(bool force);
|
||||
// [/RLVa:KB]
|
||||
|
||||
bool isRequestPending(const LLUUID& agent_id);
|
||||
|
||||
void erase(const LLUUID& agent_id);
|
||||
|
||||
/// Provide some fallback for agents that return errors
|
||||
void handleAgentError(const LLUUID& agent_id);
|
||||
|
||||
// Force a re-fetch of the most recent data, but keep the current
|
||||
// data in cache
|
||||
void fetch(const LLUUID& agent_id);
|
||||
|
||||
@@ -43,10 +43,7 @@
|
||||
#include "lluuid.h"
|
||||
#include "message.h"
|
||||
|
||||
// Constants
|
||||
static const std::string CN_WAITING("(Loading...)"); // *TODO: translate
|
||||
static const std::string CN_NOBODY("(nobody)"); // *TODO: translate
|
||||
static const std::string CN_NONE("(none)"); // *TODO: translate
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
// llsd serialization constants
|
||||
static const std::string AGENTS("agents");
|
||||
@@ -65,6 +62,7 @@ const S32 CN_FILE_VERSION = 2;
|
||||
|
||||
// Globals
|
||||
LLCacheName* gCacheName = NULL;
|
||||
std::map<std::string, std::string> LLCacheName::sCacheName;
|
||||
|
||||
/// ---------------------------------------------------------------------------
|
||||
/// class LLCacheNameEntry
|
||||
@@ -84,6 +82,8 @@ public:
|
||||
};
|
||||
|
||||
LLCacheNameEntry::LLCacheNameEntry()
|
||||
: mIsGroup(false),
|
||||
mCreateTime(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -92,17 +92,19 @@ class PendingReply
|
||||
{
|
||||
public:
|
||||
LLUUID mID;
|
||||
LLCacheNameCallback mCallback;
|
||||
LLCacheNameSignal mSignal;
|
||||
LLHost mHost;
|
||||
void* mData;
|
||||
PendingReply(const LLUUID& id, LLCacheNameCallback callback, void* data = NULL)
|
||||
: mID(id), mCallback(callback), mData(data)
|
||||
{ }
|
||||
|
||||
|
||||
PendingReply(const LLUUID& id, const LLHost& host)
|
||||
: mID(id), mCallback(0), mHost(host)
|
||||
{ }
|
||||
|
||||
: mID(id), mHost(host)
|
||||
{
|
||||
}
|
||||
|
||||
boost::signals2::connection setCallback(const LLCacheNameCallback& cb)
|
||||
{
|
||||
return mSignal.connect(cb);
|
||||
}
|
||||
|
||||
void done() { mID.setNull(); }
|
||||
bool isDone() const { return mID.isNull() != FALSE; }
|
||||
};
|
||||
@@ -126,7 +128,7 @@ private:
|
||||
};
|
||||
|
||||
ReplySender::ReplySender(LLMessageSystem* msg)
|
||||
: mMsg(msg), mPending(false)
|
||||
: mMsg(msg), mPending(false), mCurrIsGroup(false)
|
||||
{ }
|
||||
|
||||
ReplySender::~ReplySender()
|
||||
@@ -187,10 +189,10 @@ void ReplySender::flush()
|
||||
|
||||
|
||||
typedef std::set<LLUUID> AskQueue;
|
||||
typedef std::vector<PendingReply> ReplyQueue;
|
||||
typedef std::list<PendingReply*> ReplyQueue;
|
||||
typedef std::map<LLUUID,U32> PendingQueue;
|
||||
typedef std::map<LLUUID, LLCacheNameEntry*> Cache;
|
||||
typedef std::vector<LLCacheNameCallback> Observers;
|
||||
typedef std::map<std::string, LLUUID> ReverseCache;
|
||||
|
||||
class LLCacheName::Impl
|
||||
{
|
||||
@@ -200,7 +202,9 @@ public:
|
||||
|
||||
Cache mCache;
|
||||
// the map of UUIDs to names
|
||||
|
||||
ReverseCache mReverseCache;
|
||||
// map of names to UUIDs
|
||||
|
||||
AskQueue mAskNameQueue;
|
||||
AskQueue mAskGroupQueue;
|
||||
// UUIDs to ask our upstream host about
|
||||
@@ -211,13 +215,18 @@ public:
|
||||
ReplyQueue mReplyQueue;
|
||||
// requests awaiting replies from us
|
||||
|
||||
Observers mObservers;
|
||||
LLCacheNameSignal mSignal;
|
||||
|
||||
LLFrameTimer mProcessTimer;
|
||||
|
||||
Impl(LLMessageSystem* msg);
|
||||
~Impl();
|
||||
|
||||
BOOL getName(const LLUUID& id, std::string& first, std::string& last);
|
||||
|
||||
boost::signals2::connection addPending(const LLUUID& id, const LLCacheNameCallback& callback);
|
||||
void addPending(const LLUUID& id, const LLHost& host);
|
||||
|
||||
void processPendingAsks();
|
||||
void processPendingReplies();
|
||||
void sendRequest(const char* msg_name, const AskQueue& queue);
|
||||
@@ -231,8 +240,6 @@ public:
|
||||
static void handleUUIDNameReply(LLMessageSystem* msg, void** userdata);
|
||||
static void handleUUIDGroupNameRequest(LLMessageSystem* msg, void** userdata);
|
||||
static void handleUUIDGroupNameReply(LLMessageSystem* msg, void** userdata);
|
||||
|
||||
void notifyObservers(const LLUUID& id, const std::string& first, const std::string& last, BOOL group);
|
||||
};
|
||||
|
||||
|
||||
@@ -247,6 +254,9 @@ LLCacheName::LLCacheName(LLMessageSystem* msg)
|
||||
LLCacheName::LLCacheName(LLMessageSystem* msg, const LLHost& upstream_host)
|
||||
: impl(* new Impl(msg))
|
||||
{
|
||||
sCacheName["waiting"] = "(Loading...)";
|
||||
sCacheName["nobody"] = "(nobody)";
|
||||
sCacheName["none"] = "(none)";
|
||||
setUpstream(upstream_host);
|
||||
}
|
||||
|
||||
@@ -272,54 +282,34 @@ LLCacheName::Impl::Impl(LLMessageSystem* msg)
|
||||
LLCacheName::Impl::~Impl()
|
||||
{
|
||||
for_each(mCache.begin(), mCache.end(), DeletePairedPointer());
|
||||
for_each(mReplyQueue.begin(), mReplyQueue.end(), DeletePointer());
|
||||
}
|
||||
|
||||
boost::signals2::connection LLCacheName::Impl::addPending(const LLUUID& id, const LLCacheNameCallback& callback)
|
||||
{
|
||||
PendingReply* reply = new PendingReply(id, LLHost());
|
||||
boost::signals2::connection res = reply->setCallback(callback);
|
||||
mReplyQueue.push_back(reply);
|
||||
return res;
|
||||
}
|
||||
|
||||
void LLCacheName::Impl::addPending(const LLUUID& id, const LLHost& host)
|
||||
{
|
||||
PendingReply* reply = new PendingReply(id, host);
|
||||
mReplyQueue.push_back(reply);
|
||||
}
|
||||
|
||||
void LLCacheName::setUpstream(const LLHost& upstream_host)
|
||||
{
|
||||
impl.mUpstreamHost = upstream_host;
|
||||
}
|
||||
|
||||
void LLCacheName::addObserver(LLCacheNameCallback callback)
|
||||
boost::signals2::connection LLCacheName::addObserver(const LLCacheNameCallback& callback)
|
||||
{
|
||||
impl.mObservers.push_back(callback);
|
||||
}
|
||||
|
||||
void LLCacheName::removeObserver(LLCacheNameCallback callback)
|
||||
{
|
||||
Observers::iterator it = impl.mObservers.begin();
|
||||
Observers::iterator end = impl.mObservers.end();
|
||||
|
||||
for ( ; it != end; ++it)
|
||||
{
|
||||
const LLCacheNameCallback& cb = (*it);
|
||||
if (cb == callback)
|
||||
{
|
||||
impl.mObservers.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLCacheName::cancelCallback(const LLUUID& id, LLCacheNameCallback callback, void* user_data)
|
||||
{
|
||||
ReplyQueue::iterator it = impl.mReplyQueue.begin();
|
||||
ReplyQueue::iterator end = impl.mReplyQueue.end();
|
||||
|
||||
for(; it != end; ++it)
|
||||
{
|
||||
const PendingReply& reply = (*it);
|
||||
|
||||
if ((callback == reply.mCallback)
|
||||
&& (id == reply.mID)
|
||||
&& (user_data == reply.mData) )
|
||||
{
|
||||
impl.mReplyQueue.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return impl.mSignal.connect(callback);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void LLCacheName::importFile(LLFILE* fp)
|
||||
{
|
||||
S32 count = 0;
|
||||
@@ -396,11 +386,12 @@ void LLCacheName::importFile(LLFILE* fp)
|
||||
|
||||
llinfos << "LLCacheName loaded " << count << " names" << llendl;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool LLCacheName::importFile(std::istream& istr)
|
||||
{
|
||||
LLSD data;
|
||||
if(LLSDSerialize::fromXML(data, istr) < 1)
|
||||
if(LLSDSerialize::fromXMLDocument(data, istr) < 1)
|
||||
return false;
|
||||
|
||||
// We'll expire entries more than a week old
|
||||
@@ -426,6 +417,9 @@ bool LLCacheName::importFile(std::istream& istr)
|
||||
entry->mFirstName = agent[FIRST].asString();
|
||||
entry->mLastName = agent[LAST].asString();
|
||||
impl.mCache[id] = entry;
|
||||
std::string fullname = buildFullName(entry->mFirstName, entry->mLastName);
|
||||
impl.mReverseCache[fullname] = id;
|
||||
|
||||
++count;
|
||||
}
|
||||
llinfos << "LLCacheName loaded " << count << " agent names" << llendl;
|
||||
@@ -446,6 +440,7 @@ bool LLCacheName::importFile(std::istream& istr)
|
||||
entry->mCreateTime = ctime;
|
||||
entry->mGroupName = group[NAME].asString();
|
||||
impl.mCache[id] = entry;
|
||||
impl.mReverseCache[entry->mGroupName] = id;
|
||||
++count;
|
||||
}
|
||||
llinfos << "LLCacheName loaded " << count << " group names" << llendl;
|
||||
@@ -488,16 +483,16 @@ void LLCacheName::exportFile(std::ostream& ostr)
|
||||
}
|
||||
|
||||
|
||||
BOOL LLCacheName::getName(const LLUUID& id, std::string& first, std::string& last)
|
||||
BOOL LLCacheName::Impl::getName(const LLUUID& id, std::string& first, std::string& last)
|
||||
{
|
||||
if(id.isNull())
|
||||
{
|
||||
first = CN_NOBODY;
|
||||
first = sCacheName["nobody"];
|
||||
last.clear();
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
|
||||
LLCacheNameEntry* entry = get_ptr_in_map(mCache, id );
|
||||
if (entry)
|
||||
{
|
||||
first = entry->mFirstName;
|
||||
@@ -506,32 +501,42 @@ BOOL LLCacheName::getName(const LLUUID& id, std::string& first, std::string& las
|
||||
}
|
||||
else
|
||||
{
|
||||
first = CN_WAITING;
|
||||
first = sCacheName["waiting"];
|
||||
last.clear();
|
||||
if (!impl.isRequestPending(id))
|
||||
if (!isRequestPending(id))
|
||||
{
|
||||
impl.mAskNameQueue.insert(id);
|
||||
mAskNameQueue.insert(id);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// static
|
||||
void LLCacheName::localizeCacheName(std::string key, std::string value)
|
||||
{
|
||||
if (key!="" && value!= "" )
|
||||
sCacheName[key]=value;
|
||||
else
|
||||
llwarns<< " Error localizing cache key " << key << " To "<< value<<llendl;
|
||||
}
|
||||
|
||||
BOOL LLCacheName::getFullName(const LLUUID& id, std::string& fullname)
|
||||
{
|
||||
std::string first_name, last_name;
|
||||
BOOL res = getName(id, first_name, last_name);
|
||||
if(res)
|
||||
fullname = first_name + " " + last_name;
|
||||
BOOL res = impl.getName(id, first_name, last_name);
|
||||
fullname = buildFullName(first_name, last_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group)
|
||||
{
|
||||
if(id.isNull())
|
||||
{
|
||||
group = CN_NONE;
|
||||
return FALSE;
|
||||
group = sCacheName["none"];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache,id);
|
||||
@@ -551,7 +556,7 @@ BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group)
|
||||
}
|
||||
else
|
||||
{
|
||||
group = CN_WAITING;
|
||||
group = sCacheName["waiting"];
|
||||
if (!impl.isRequestPending(id))
|
||||
{
|
||||
impl.mAskGroupQueue.insert(id);
|
||||
@@ -560,27 +565,151 @@ BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make the cache name callback take a SINGLE std::string,
|
||||
// not a separate first and last name.
|
||||
void LLCacheName::get(const LLUUID& id, BOOL is_group, LLCacheNameCallback callback, void* user_data)
|
||||
BOOL LLCacheName::getUUID(const std::string& first, const std::string& last, LLUUID& id)
|
||||
{
|
||||
std::string full_name = buildFullName(first, last);
|
||||
return getUUID(full_name, id);
|
||||
}
|
||||
|
||||
BOOL LLCacheName::getUUID(const std::string& full_name, LLUUID& id)
|
||||
{
|
||||
ReverseCache::iterator iter = impl.mReverseCache.find(full_name);
|
||||
if (iter != impl.mReverseCache.end())
|
||||
{
|
||||
id = iter->second;
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
std::string LLCacheName::buildFullName(const std::string& first, const std::string& last)
|
||||
{
|
||||
std::string fullname = first;
|
||||
if (!last.empty()
|
||||
&& last != "Resident")
|
||||
{
|
||||
fullname += ' ';
|
||||
fullname += last;
|
||||
}
|
||||
return fullname;
|
||||
}
|
||||
|
||||
//static
|
||||
std::string LLCacheName::cleanFullName(const std::string& full_name)
|
||||
{
|
||||
return full_name.substr(0, full_name.find(" Resident"));
|
||||
}
|
||||
|
||||
//static
|
||||
std::string LLCacheName::buildUsername(const std::string& full_name)
|
||||
{
|
||||
// rare, but handle hard-coded error names returned from server
|
||||
if (full_name == "(\?\?\?) (\?\?\?)")
|
||||
{
|
||||
return "(\?\?\?)";
|
||||
}
|
||||
|
||||
std::string::size_type index = full_name.find(' ');
|
||||
|
||||
if (index != std::string::npos)
|
||||
{
|
||||
std::string username;
|
||||
username = full_name.substr(0, index);
|
||||
std::string lastname = full_name.substr(index+1);
|
||||
|
||||
if (lastname != "Resident")
|
||||
{
|
||||
username = username + "." + lastname;
|
||||
}
|
||||
|
||||
LLStringUtil::toLower(username);
|
||||
return username;
|
||||
}
|
||||
|
||||
// if the input wasn't a correctly formatted legacy name just return it unchanged
|
||||
return full_name;
|
||||
}
|
||||
|
||||
//static
|
||||
std::string LLCacheName::buildLegacyName(const std::string& complete_name)
|
||||
{
|
||||
//boost::regexp was showing up in the crashreporter, so doing
|
||||
//painfully manual parsing using substr. LF
|
||||
S32 open_paren = complete_name.rfind(" (");
|
||||
S32 close_paren = complete_name.rfind(')');
|
||||
|
||||
if (open_paren != std::string::npos &&
|
||||
close_paren == complete_name.length()-1)
|
||||
{
|
||||
S32 length = close_paren - open_paren - 2;
|
||||
std::string legacy_name = complete_name.substr(open_paren+2, length);
|
||||
|
||||
if (legacy_name.length() > 0)
|
||||
{
|
||||
std::string cap_letter = legacy_name.substr(0, 1);
|
||||
LLStringUtil::toUpper(cap_letter);
|
||||
legacy_name = cap_letter + legacy_name.substr(1);
|
||||
|
||||
S32 separator = legacy_name.find('.');
|
||||
|
||||
if (separator != std::string::npos)
|
||||
{
|
||||
std::string last_name = legacy_name.substr(separator+1);
|
||||
legacy_name = legacy_name.substr(0, separator);
|
||||
|
||||
if (last_name.length() > 0)
|
||||
{
|
||||
cap_letter = last_name.substr(0, 1);
|
||||
LLStringUtil::toUpper(cap_letter);
|
||||
legacy_name = legacy_name + " " + cap_letter + last_name.substr(1);
|
||||
}
|
||||
}
|
||||
|
||||
return legacy_name;
|
||||
}
|
||||
}
|
||||
|
||||
return complete_name;
|
||||
}
|
||||
|
||||
// This is a little bit kludgy. LLCacheNameCallback is a slot instead of a function pointer.
|
||||
// The reason it is a slot is so that the legacy get() function below can bind an old callback
|
||||
// and pass it as a slot. The reason it isn't a boost::function is so that trackable behavior
|
||||
// doesn't get lost. As a result, we have to bind the slot to a signal to call it, even when
|
||||
// we call it immediately. -Steve
|
||||
// NOTE: Even though passing first and last name is a bit of extra overhead, it eliminates the
|
||||
// potential need for any parsing should any code need to handle first and last name independently.
|
||||
boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback)
|
||||
{
|
||||
boost::signals2::connection res;
|
||||
|
||||
if(id.isNull())
|
||||
{
|
||||
callback(id, CN_NOBODY, "", is_group, user_data);
|
||||
return;
|
||||
LLCacheNameSignal signal;
|
||||
signal.connect(callback);
|
||||
signal(id, sCacheName["nobody"], is_group);
|
||||
return res;
|
||||
}
|
||||
|
||||
LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
|
||||
if (entry)
|
||||
{
|
||||
LLCacheNameSignal signal;
|
||||
signal.connect(callback);
|
||||
// id found in map therefore we can call the callback immediately.
|
||||
if (entry->mIsGroup)
|
||||
{
|
||||
callback(id, entry->mGroupName, "", entry->mIsGroup, user_data);
|
||||
signal(id, entry->mGroupName, entry->mIsGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(id, entry->mFirstName, entry->mLastName, entry->mIsGroup, user_data);
|
||||
std::string fullname =
|
||||
buildFullName(entry->mFirstName, entry->mLastName);
|
||||
signal(id, fullname, entry->mIsGroup);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -597,16 +726,29 @@ void LLCacheName::get(const LLUUID& id, BOOL is_group, LLCacheNameCallback callb
|
||||
impl.mAskNameQueue.insert(id);
|
||||
}
|
||||
}
|
||||
impl.mReplyQueue.push_back(PendingReply(id, callback, user_data));
|
||||
res = impl.addPending(id, callback);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
boost::signals2::connection LLCacheName::getGroup(const LLUUID& group_id,
|
||||
const LLCacheNameCallback& callback)
|
||||
{
|
||||
return get(group_id, true, callback);
|
||||
}
|
||||
|
||||
boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data)
|
||||
{
|
||||
return get(id, is_group, boost::bind(callback, _1, _2, _3, user_data));
|
||||
}
|
||||
|
||||
// <edit>
|
||||
BOOL LLCacheName::getIfThere(const LLUUID& id, std::string& fullname, BOOL& is_group)
|
||||
bool LLCacheName::getIfThere(const LLUUID& id, std::string& fullname, BOOL& is_group)
|
||||
{
|
||||
if(id.isNull())
|
||||
{
|
||||
fullname = "";
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
|
||||
@@ -621,10 +763,10 @@ BOOL LLCacheName::getIfThere(const LLUUID& id, std::string& fullname, BOOL& is_g
|
||||
fullname = entry->mFirstName + " " + entry->mLastName;
|
||||
}
|
||||
is_group = entry->mIsGroup;
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
fullname = "";
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
// </edit>
|
||||
void LLCacheName::processPending()
|
||||
@@ -695,7 +837,7 @@ void LLCacheName::dump()
|
||||
{
|
||||
llinfos
|
||||
<< iter->first << " = "
|
||||
<< entry->mFirstName << " " << entry->mLastName
|
||||
<< buildFullName(entry->mFirstName, entry->mLastName)
|
||||
<< " @ " << entry->mCreateTime
|
||||
<< llendl;
|
||||
}
|
||||
@@ -710,16 +852,27 @@ void LLCacheName::dumpStats()
|
||||
<< " AskGroup=" << impl.mAskGroupQueue.size()
|
||||
<< " Pending=" << impl.mPendingQueue.size()
|
||||
<< " Reply=" << impl.mReplyQueue.size()
|
||||
<< " Observers=" << impl.mObservers.size()
|
||||
// << " Observers=" << impl.mSignal.size()
|
||||
<< llendl;
|
||||
}
|
||||
|
||||
void LLCacheName::clear()
|
||||
{
|
||||
for_each(impl.mCache.begin(), impl.mCache.end(), DeletePairedPointer());
|
||||
impl.mCache.clear();
|
||||
}
|
||||
|
||||
//static
|
||||
std::string LLCacheName::getDefaultName()
|
||||
{
|
||||
return CN_WAITING;
|
||||
return sCacheName["waiting"];
|
||||
}
|
||||
|
||||
//static
|
||||
std::string LLCacheName::getDefaultLastName()
|
||||
{
|
||||
return "Resident";
|
||||
}
|
||||
void LLCacheName::Impl::processPendingAsks()
|
||||
{
|
||||
sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue);
|
||||
@@ -730,50 +883,51 @@ void LLCacheName::Impl::processPendingAsks()
|
||||
|
||||
void LLCacheName::Impl::processPendingReplies()
|
||||
{
|
||||
ReplyQueue::iterator it = mReplyQueue.begin();
|
||||
ReplyQueue::iterator end = mReplyQueue.end();
|
||||
|
||||
// First call all the callbacks, because they might send messages.
|
||||
for(; it != end; ++it)
|
||||
for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
|
||||
{
|
||||
LLCacheNameEntry* entry = get_ptr_in_map(mCache, it->mID);
|
||||
PendingReply* reply = *it;
|
||||
LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID);
|
||||
if(!entry) continue;
|
||||
|
||||
if (it->mCallback)
|
||||
if (!entry->mIsGroup)
|
||||
{
|
||||
if (!entry->mIsGroup)
|
||||
{
|
||||
(it->mCallback)(it->mID,
|
||||
entry->mFirstName, entry->mLastName,
|
||||
FALSE, it->mData);
|
||||
}
|
||||
else {
|
||||
(it->mCallback)(it->mID,
|
||||
entry->mGroupName, "",
|
||||
TRUE, it->mData);
|
||||
}
|
||||
std::string fullname =
|
||||
LLCacheName::buildFullName(entry->mFirstName, entry->mLastName);
|
||||
(reply->mSignal)(reply->mID, fullname, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
(reply->mSignal)(reply->mID, entry->mGroupName, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Forward on all replies, if needed.
|
||||
ReplySender sender(mMsg);
|
||||
for (it = mReplyQueue.begin(); it != end; ++it)
|
||||
for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
|
||||
{
|
||||
LLCacheNameEntry* entry = get_ptr_in_map(mCache, it->mID);
|
||||
PendingReply* reply = *it;
|
||||
LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID);
|
||||
if(!entry) continue;
|
||||
|
||||
if (it->mHost.isOk())
|
||||
if (reply->mHost.isOk())
|
||||
{
|
||||
sender.send(it->mID, *entry, it->mHost);
|
||||
sender.send(reply->mID, *entry, reply->mHost);
|
||||
}
|
||||
|
||||
it->done();
|
||||
reply->done();
|
||||
}
|
||||
|
||||
for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); )
|
||||
{
|
||||
ReplyQueue::iterator curit = it++;
|
||||
PendingReply* reply = *curit;
|
||||
if (reply->isDone())
|
||||
{
|
||||
delete reply;
|
||||
mReplyQueue.erase(curit);
|
||||
}
|
||||
}
|
||||
|
||||
mReplyQueue.erase(
|
||||
remove_if(mReplyQueue.begin(), mReplyQueue.end(),
|
||||
std::mem_fun_ref(&PendingReply::isDone)),
|
||||
mReplyQueue.end());
|
||||
}
|
||||
|
||||
|
||||
@@ -811,18 +965,6 @@ void LLCacheName::Impl::sendRequest(
|
||||
}
|
||||
}
|
||||
|
||||
void LLCacheName::Impl::notifyObservers(const LLUUID& id,
|
||||
const std::string& first, const std::string& last, BOOL is_group)
|
||||
{
|
||||
for (Observers::const_iterator i = mObservers.begin(),
|
||||
end = mObservers.end();
|
||||
i != end;
|
||||
++i)
|
||||
{
|
||||
(**i)(id, first, last, is_group, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bool LLCacheName::Impl::isRequestPending(const LLUUID& id)
|
||||
{
|
||||
U32 now = (U32)time(NULL);
|
||||
@@ -889,7 +1031,7 @@ void LLCacheName::Impl::processUUIDRequest(LLMessageSystem* msg, bool isGroup)
|
||||
}
|
||||
}
|
||||
|
||||
mReplyQueue.push_back(PendingReply(id, fromHost));
|
||||
addPending(id, fromHost);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -927,11 +1069,32 @@ void LLCacheName::Impl::processUUIDReply(LLMessageSystem* msg, bool isGroup)
|
||||
|
||||
if (!isGroup)
|
||||
{
|
||||
notifyObservers(id, entry->mFirstName, entry->mLastName, FALSE);
|
||||
// NOTE: Very occasionally the server sends down a full name
|
||||
// in the first name field with an empty last name, for example,
|
||||
// first = "Ladanie1 Resident", last = "".
|
||||
// I cannot reproduce this, nor can I find a bug in the server code.
|
||||
// Ensure "Resident" does not appear via cleanFullName, because
|
||||
// buildFullName only checks last name. JC
|
||||
std::string full_name;
|
||||
if (entry->mLastName.empty())
|
||||
{
|
||||
full_name = cleanFullName(entry->mFirstName);
|
||||
|
||||
//fix what we are putting in the cache
|
||||
entry->mFirstName = full_name;
|
||||
entry->mLastName = "Resident";
|
||||
}
|
||||
else
|
||||
{
|
||||
full_name = LLCacheName::buildFullName(entry->mFirstName, entry->mLastName);
|
||||
}
|
||||
mSignal(id, full_name, false);
|
||||
mReverseCache[full_name] = id;
|
||||
}
|
||||
else
|
||||
{
|
||||
notifyObservers(id, entry->mGroupName, "", TRUE);
|
||||
mSignal(id, entry->mGroupName, true);
|
||||
mReverseCache[entry->mGroupName] = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,12 +33,21 @@
|
||||
#ifndef LL_LLCACHENAME_H
|
||||
#define LL_LLCACHENAME_H
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
class LLMessageSystem;
|
||||
class LLHost;
|
||||
class LLUUID;
|
||||
|
||||
// agent_id/group_id, first_name, last_name, is_group, user_data
|
||||
typedef void (*LLCacheNameCallback)(const LLUUID&, const std::string&, const std::string&, BOOL, void*);
|
||||
|
||||
typedef boost::signals2::signal<void (const LLUUID& id,
|
||||
const std::string& name,
|
||||
bool is_group)> LLCacheNameSignal;
|
||||
typedef LLCacheNameSignal::slot_type LLCacheNameCallback;
|
||||
|
||||
// Old callback with user data for compatability
|
||||
typedef void (*old_callback_t)(const LLUUID&, const std::string&, bool, void*);
|
||||
|
||||
// Here's the theory:
|
||||
// If you request a name that isn't in the cache, it returns "waiting"
|
||||
@@ -59,25 +68,44 @@ public:
|
||||
// for simulators, this is the data server
|
||||
void setUpstream(const LLHost& upstream_host);
|
||||
|
||||
void addObserver(LLCacheNameCallback callback);
|
||||
void removeObserver(LLCacheNameCallback callback);
|
||||
|
||||
void cancelCallback(const LLUUID& id, LLCacheNameCallback callback, void* user_data = NULL);
|
||||
boost::signals2::connection addObserver(const LLCacheNameCallback& callback);
|
||||
|
||||
// janky old format. Remove after a while. Phoenix. 2008-01-30
|
||||
#if 0
|
||||
void importFile(LLFILE* fp);
|
||||
#endif
|
||||
|
||||
// storing cache on disk; for viewer, in name.cache
|
||||
bool importFile(std::istream& istr);
|
||||
void exportFile(std::ostream& ostr);
|
||||
|
||||
// If available, copies the first and last name into the strings provided.
|
||||
// first must be at least DB_FIRST_NAME_BUF_SIZE characters.
|
||||
// last must be at least DB_LAST_NAME_BUF_SIZE characters.
|
||||
// If available, copies name ("bobsmith123" or "James Linden") into string
|
||||
// If not available, copies the string "waiting".
|
||||
// Returns TRUE iff available.
|
||||
BOOL getName(const LLUUID& id, std::string& first, std::string& last);
|
||||
BOOL getFullName(const LLUUID& id, std::string& fullname);
|
||||
BOOL getFullName(const LLUUID& id, std::string& full_name);
|
||||
|
||||
// Reverse lookup of UUID from name
|
||||
BOOL getUUID(const std::string& first, const std::string& last, LLUUID& id);
|
||||
BOOL getUUID(const std::string& fullname, LLUUID& id);
|
||||
|
||||
// IDEVO Temporary code
|
||||
// Clean up new-style "bobsmith123 Resident" names to "bobsmith123" for display
|
||||
static std::string buildFullName(const std::string& first, const std::string& last);
|
||||
|
||||
// Clean up legacy "bobsmith123 Resident" to "bobsmith123"
|
||||
// If name does not contain "Resident" returns it unchanged.
|
||||
static std::string cleanFullName(const std::string& full_name);
|
||||
|
||||
// Converts a standard legacy name to a username
|
||||
// "bobsmith123 Resident" -> "bobsmith"
|
||||
// "Random Linden" -> "random.linden"
|
||||
static std::string buildUsername(const std::string& name);
|
||||
|
||||
// Converts a complete display name to a legacy name
|
||||
// if possible, otherwise returns the input
|
||||
// "Alias (random.linden)" -> "Random Linden"
|
||||
// "Something random" -> "Something random"
|
||||
static std::string buildLegacyName(const std::string& name);
|
||||
|
||||
// If available, this method copies the group name into the string
|
||||
// provided. The caller must allocate at least
|
||||
@@ -89,16 +117,19 @@ public:
|
||||
// If the data is currently available, may call the callback immediatly
|
||||
// otherwise, will request the data, and will call the callback when
|
||||
// available. There is no garuntee the callback will ever be called.
|
||||
void get(const LLUUID& id, BOOL is_group, LLCacheNameCallback callback, void* user_data = NULL);
|
||||
boost::signals2::connection get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback);
|
||||
|
||||
// <edit>
|
||||
BOOL getIfThere(const LLUUID& id, std::string& fullname, BOOL& is_group);
|
||||
// </edit>
|
||||
//<edit>
|
||||
bool getIfThere(const LLUUID& id, std::string& fullname, BOOL& is_group);
|
||||
//</edit>
|
||||
|
||||
// Convenience method for looking up a group name, so you can
|
||||
// tell the difference between avatar lookup and group lookup
|
||||
// in global searches
|
||||
boost::signals2::connection getGroup(const LLUUID& group_id, const LLCacheNameCallback& callback);
|
||||
|
||||
// LEGACY
|
||||
void getName(const LLUUID& id, LLCacheNameCallback callback, void* user_data = NULL)
|
||||
{ get(id, FALSE, callback, user_data); }
|
||||
|
||||
boost::signals2::connection get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data);
|
||||
// This method needs to be called from time to time to send out
|
||||
// requests.
|
||||
void processPending();
|
||||
@@ -109,9 +140,16 @@ public:
|
||||
// Debugging
|
||||
void dump(); // Dumps the contents of the cache
|
||||
void dumpStats(); // Dumps the sizes of the cache and associated queues.
|
||||
void clear(); // Deletes all entries from the cache
|
||||
|
||||
static std::string getDefaultName();
|
||||
|
||||
// Returns "Resident", the default last name for SLID-based accounts
|
||||
// that have no last name.
|
||||
static std::string getDefaultLastName();
|
||||
|
||||
static void localizeCacheName(std::string key, std::string value);
|
||||
static std::map<std::string, std::string> sCacheName;
|
||||
private:
|
||||
|
||||
class Impl;
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
|
||||
LLMeanCollisionData(LLMeanCollisionData *mcd)
|
||||
: mVictim(mcd->mVictim), mPerp(mcd->mPerp), mTime(mcd->mTime), mType(mcd->mType), mMag(mcd->mMag),
|
||||
mFirstName(mcd->mFirstName), mLastName(mcd->mLastName)
|
||||
mFullName(mcd->mFullName)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -95,8 +95,7 @@ public:
|
||||
time_t mTime;
|
||||
EMeanCollisionType mType;
|
||||
F32 mMag;
|
||||
std::string mFirstName;
|
||||
std::string mLastName;
|
||||
std::string mFullName;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user