Display names from Phoenix. Thanks Wolfspirit!

That's quite a piece of work. I have NO idea how Jess can downplay the
effort that went into this.

And it's still not complete, local chat is not affected, perhaps group
chat too.
This commit is contained in:
Siana Gearz
2010-11-20 06:01:09 +01:00
parent a80470865f
commit 6866a36a5c
31 changed files with 2151 additions and 27 deletions

View File

@@ -572,6 +572,22 @@
<boolean>false</boolean>
</map>
<!-- Server to client -->
<key>DisplayNameUpdate</key>
<map>
<key>flavor</key>
<string>llsd</string>
<key>trusted-sender</key>
<boolean>true</boolean>
</map>
<key>SetDisplayNameReply</key>
<map>
<key>flavor</key>
<string>llsd</string>
<key>trusted-sender</key>
<boolean>true</boolean>
</map>
</map>
<key>capBans</key>

View File

@@ -15,6 +15,7 @@ set(llcommon_SOURCE_FILES
llapp.cpp
llapr.cpp
llassettype.cpp
llavatarname.cpp
llbase32.cpp
llbase64.cpp
llcommon.cpp
@@ -85,6 +86,7 @@ set(llcommon_HEADER_FILES
llassettype.h
llassoclist.h
llavatarconstants.h
llavatarname.h
llbase32.h
llbase64.h
llboost.h

View File

@@ -0,0 +1,117 @@
/**
* @file llavatarname.cpp
* @brief Represents name-related data for an avatar, such as the
* username/SLID ("bobsmith123" or "james.linden") and the display
* name ("James Cook")
*
* $LicenseInfo:firstyear=2010&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$
*/
/*
Ported to Phoenix by Wolfspirit Magic.
*/
#include "linden_common.h"
#include "llavatarname.h"
#include "lldate.h"
#include "llsd.h"
// Store these in pre-built std::strings to avoid memory allocations in
// LLSD map lookups
static const std::string USERNAME("username");
static const std::string DISPLAY_NAME("display_name");
static const std::string LEGACY_FIRST_NAME("legacy_first_name");
static const std::string LEGACY_LAST_NAME("legacy_last_name");
static const std::string IS_DISPLAY_NAME_DEFAULT("is_display_name_default");
static const std::string DISPLAY_NAME_EXPIRES("display_name_expires");
static const std::string DISPLAY_NAME_NEXT_UPDATE("display_name_next_update");
LLAvatarName::LLAvatarName()
: mUsername(),
mDisplayName(),
mLegacyFirstName(),
mLegacyLastName(),
mIsDisplayNameDefault(false),
mIsDummy(false),
mExpires(F64_MAX),
mNextUpdate(0.0)
{ }
bool LLAvatarName::operator<(const LLAvatarName& rhs) const
{
if (mUsername == rhs.mUsername)
return mDisplayName < rhs.mDisplayName;
else
return mUsername < rhs.mUsername;
}
LLSD LLAvatarName::asLLSD() const
{
LLSD sd;
sd[USERNAME] = mUsername;
sd[DISPLAY_NAME] = mDisplayName;
sd[LEGACY_FIRST_NAME] = mLegacyFirstName;
sd[LEGACY_LAST_NAME] = mLegacyLastName;
sd[IS_DISPLAY_NAME_DEFAULT] = mIsDisplayNameDefault;
sd[DISPLAY_NAME_EXPIRES] = LLDate(mExpires);
sd[DISPLAY_NAME_NEXT_UPDATE] = LLDate(mNextUpdate);
return sd;
}
void LLAvatarName::fromLLSD(const LLSD& sd)
{
mUsername = sd[USERNAME].asString();
mDisplayName = sd[DISPLAY_NAME].asString();
mLegacyFirstName = sd[LEGACY_FIRST_NAME].asString();
mLegacyLastName = sd[LEGACY_LAST_NAME].asString();
mIsDisplayNameDefault = sd[IS_DISPLAY_NAME_DEFAULT].asBoolean();
LLDate expires = sd[DISPLAY_NAME_EXPIRES];
mExpires = expires.secondsSinceEpoch();
LLDate next_update = sd[DISPLAY_NAME_NEXT_UPDATE];
mNextUpdate = next_update.secondsSinceEpoch();
}
std::string LLAvatarName::getCompleteName() const
{
std::string name;
if (!mUsername.empty())
{
name = mDisplayName + " (" + mUsername + ")";
}
else
{
// ...display names are off, legacy name is in mDisplayName
name = mDisplayName;
}
return name;
}
std::string LLAvatarName::getLegacyName() const
{
std::string name;
name.reserve( mLegacyFirstName.size() + 1 + mLegacyLastName.size() );
name = mLegacyFirstName;
name += " ";
name += mLegacyLastName;
return name;
}

View File

@@ -0,0 +1,95 @@
/**
* @file llavatarname.h
* @brief Represents name-related data for an avatar, such as the
* username/SLID ("bobsmith123" or "james.linden") and the display
* name ("James Cook")
*
* $LicenseInfo:firstyear=2010&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$
*/
#ifndef LLAVATARNAME_H
#define LLAVATARNAME_H
#include <string>
class LLSD;
class LLAvatarName
{
public:
LLAvatarName();
bool operator<(const LLAvatarName& rhs) const;
LLSD asLLSD() const;
void fromLLSD(const LLSD& sd);
// For normal names, returns "James Linden (james.linden)"
// When display names are disabled returns just "James Linden"
std::string getCompleteName() const;
// Returns "James Linden" or "bobsmith123 Resident" for backwards
// compatibility with systems like voice and muting
// *TODO: Eliminate this in favor of username only
std::string getLegacyName() const;
// "bobsmith123" or "james.linden", US-ASCII only
std::string mUsername;
// "Jose' Sanchez" or "James Linden", UTF-8 encoded Unicode
// Contains data whether or not user has explicitly set
// a display name; may duplicate their username.
std::string mDisplayName;
// For "James Linden", "James"
// For "bobsmith123", "bobsmith123"
// Used to communicate with legacy systems like voice and muting which
// rely on old-style names.
// *TODO: Eliminate this in favor of username only
std::string mLegacyFirstName;
// For "James Linden", "Linden"
// For "bobsmith123", "Resident"
// see above for rationale
std::string mLegacyLastName;
// If true, both display name and SLID were generated from
// a legacy first and last name, like "James Linden (james.linden)"
bool mIsDisplayNameDefault;
// Under error conditions, we may insert "dummy" records with
// names like "???" into caches as placeholders. These can be
// shown in UI, but are not serialized.
bool mIsDummy;
// Names can change, so need to keep track of when name was
// last checked.
// Unix time-from-epoch seconds for efficiency
F64 mExpires;
// You can only change your name every N hours, so record
// when the next update is allowed
// Unix time-from-epoch seconds
F64 mNextUpdate;
};
#endif

View File

@@ -22,6 +22,7 @@ include_directories(
set(llmessage_SOURCE_FILES
llares.cpp
llassetstorage.cpp
llavatarnamecache.cpp
llblowfishcipher.cpp
llbuffer.cpp
llbufferstream.cpp
@@ -106,6 +107,7 @@ set(llmessage_HEADER_FILES
llares.h
llassetstorage.h
llavatarnamecache.h
llblowfishcipher.h
llbuffer.h
llbufferstream.h

View File

@@ -0,0 +1,826 @@
/**
* @file llavatarnamecache.cpp
* @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names
* ("James Cook") from avatar UUIDs.
*
* $LicenseInfo:firstyear=2010&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$
*/
/*
Ported to Phoenix by Wolfspirit Magic.
*/
#include "linden_common.h"
#include "llavatarnamecache.h"
#include "llcachename.h" // we wrap this system
#include "llframetimer.h"
#include "llhttpclient.h"
#include "llsd.h"
#include "llsdserialize.h"
#include <boost/tokenizer.hpp>
#include <map>
#include <set>
namespace LLAvatarNameCache
{
use_display_name_signal_t mUseDisplayNamesSignal;
// Manual override for display names - can disable even if the region
// supports it.
bool sUseDisplayNames = true;
// Cache starts in a paused state until we can determine if the
// current region supports display names.
bool sRunning = false;
// Base lookup URL for name service.
// On simulator, loaded from indra.xml
// On viewer, usually a simulator capability (at People API team's request)
// Includes the trailing slash, like "http://pdp60.lindenlab.com:8000/agents/"
std::string sNameLookupURL;
// 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, F64> 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;
// Send bulk lookup requests a few times a second at most
// only need per-frame timing resolution
LLFrameTimer sRequestTimer;
// Periodically clean out expired entries from the cache
//LLFrameTimer sEraseExpiredTimer;
//-----------------------------------------------------------------------
// Internal methods
//-----------------------------------------------------------------------
// Handle name response off network.
// Optionally skip adding to cache, used when this is a fallback to the
// legacy name system.
void processName(const LLUUID& agent_id,
const LLAvatarName& av_name,
bool add_to_cache);
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 requestNamesViaLegacy();
// Fill in an LLAvatarName with the legacy name data
void buildLegacyName(const std::string& full_name,
LLAvatarName* av_name);
// Do a single callback to a given slot
void fireSignal(const LLUUID& agent_id,
const callback_slot_t& slot,
const LLAvatarName& av_name);
// Is a request in-flight over the network?
// Erase expired names from cache
void eraseExpired();
bool expirationFromCacheControl(LLSD headers, F64 *expires);
}
/* Sample response:
<?xml version="1.0"?>
<llsd>
<map>
<key>agents</key>
<array>
<map>
<key>display_name_next_update</key>
<date>2010-04-16T21:34:02+00:00Z</date>
<key>display_name_expires</key>
<date>2010-04-16T21:32:26.142178+00:00Z</date>
<key>display_name</key>
<string>MickBot390 LLQABot</string>
<key>sl_id</key>
<string>mickbot390.llqabot</string>
<key>id</key>
<string>0012809d-7d2d-4c24-9609-af1230a37715</string>
<key>is_display_name_default</key>
<boolean>false</boolean>
</map>
<map>
<key>display_name_next_update</key>
<date>2010-04-16T21:34:02+00:00Z</date>
<key>display_name_expires</key>
<date>2010-04-16T21:32:26.142178+00:00Z</date>
<key>display_name</key>
<string>Bjork Gudmundsdottir</string>
<key>sl_id</key>
<string>sardonyx.linden</string>
<key>id</key>
<string>3941037e-78ab-45f0-b421-bd6e77c1804d</string>
<key>is_display_name_default</key>
<boolean>true</boolean>
</map>
</array>
</map>
</llsd>
*/
class LLAvatarNameResponder : public LLHTTPClient::Responder
{
private:
// need to store agent ids that are part of this request in case of
// an error, so we can flag them as unavailable
std::vector<LLUUID> mAgentIDs;
// Need the headers to look up Expires: and Retry-After:
LLSD mHeaders;
public:
LLAvatarNameResponder(const std::vector<LLUUID>& agent_ids)
: mAgentIDs(agent_ids),
mHeaders()
{ }
/*virtual*/ void completedHeader(U32 status, const std::string& reason,
const LLSD& headers)
{
mHeaders = headers;
}
/*virtual*/ void result(const LLSD& content)
{
// Pull expiration out of headers if available
F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(mHeaders);
LLSD agents = content["agents"];
LLSD::array_const_iterator it = agents.beginArray();
for ( ; it != agents.endArray(); ++it)
{
const LLSD& row = *it;
LLUUID agent_id = row["id"].asUUID();
LLAvatarName av_name;
av_name.fromLLSD(row);
// Use expiration time from header
av_name.mExpires = expires;
// Some avatars don't have explicit display names set
if (av_name.mDisplayName.empty())
{
av_name.mDisplayName = av_name.mUsername;
}
// 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)
{
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;
it = unresolved_agents.beginArray();
for ( ; it != unresolved_agents.endArray(); ++it)
{
const LLUUID& agent_id = *it;
// cache it and fire signals
LLAvatarNameCache::processName(agent_id, av_name, true);
}
}
}
/*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);
// *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
std::vector<LLUUID>::const_iterator it = mAgentIDs.begin();
for ( ; it != mAgentIDs.end(); ++it)
{
const LLUUID& agent_id = *it;
// cache it and fire signals
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;
}
}
};
void LLAvatarNameCache::processName(const LLUUID& agent_id,
const LLAvatarName& av_name,
bool add_to_cache)
{
if (add_to_cache)
{
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::requestNamesViaCapability()
{
F64 now = LLFrameTimer::getTotalSeconds();
// URL format is like:
// http://pdp60.lindenlab.com:8000/agents/?ids=3941037e-78ab-45f0-b421-bd6e77c1804d&ids=0012809d-7d2d-4c24-9609-af1230a37715&ids=0019aaba-24af-4f0a-aa72-6457953cf7f0
//
// Apache can handle URLs of 4096 chars, but let's be conservative
const U32 NAME_URL_MAX = 4096;
const U32 NAME_URL_SEND_THRESHOLD = 3000;
std::string url;
url.reserve(NAME_URL_MAX);
std::vector<LLUUID> agent_ids;
agent_ids.reserve(128);
ask_queue_t::const_iterator it = sAskQueue.begin();
for ( ; it != sAskQueue.end(); ++it)
{
const LLUUID& agent_id = *it;
if (url.empty())
{
// ...starting new request
url += sNameLookupURL;
url += "?ids=";
}
else
{
// ...continuing existing request
url += "&ids=";
}
url += agent_id.asString();
agent_ids.push_back(agent_id);
// mark request as pending
sPendingQueue[agent_id] = now;
if (url.size() > NAME_URL_SEND_THRESHOLD)
{
//llinfos << "requestNames " << url << llendl;
LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids));
url.clear();
agent_ids.clear();
}
}
if (!url.empty())
{
//llinfos << "requestNames " << url << llendl;
LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids));
url.clear();
agent_ids.clear();
}
// 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)
{
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;
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);
}
void LLAvatarNameCache::requestNamesViaLegacy()
{
F64 now = LLFrameTimer::getTotalSeconds();
std::string full_name;
ask_queue_t::const_iterator it = sAskQueue.begin();
for (; it != sAskQueue.end(); ++it)
{
const LLUUID& agent_id = *it;
// Mark as pending first, just in case the callback is immediately
// invoked below. This should never happen in practice.
sPendingQueue[agent_id] = now;
gCacheName->get(agent_id, false, legacyNameCallback);
}
// We've either answered immediately or moved all asks to the
// pending queue
sAskQueue.clear();
}
void LLAvatarNameCache::initClass(bool running)
{
sRunning = running;
}
void LLAvatarNameCache::cleanupClass()
{
}
void LLAvatarNameCache::importFile(std::istream& istr)
{
LLSD data;
S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr);
if (parse_count < 1) return;
// by convention LLSD storage is a map
// we only store one entry in the map
LLSD agents = data["agents"];
LLUUID agent_id;
LLAvatarName av_name;
LLSD::map_const_iterator it = agents.beginMap();
for ( ; it != agents.endMap(); ++it)
{
agent_id.set(it->first);
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;
}
void LLAvatarNameCache::exportFile(std::ostream& ostr)
{
LLSD agents;
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)
{
// key must be a string
agents[agent_id.asString()] = av_name.asLLSD();
}
}
LLSD data;
data["agents"] = agents;
LLSDSerialize::toPrettyXML(data, ostr);
}
void LLAvatarNameCache::setNameLookupURL(const std::string& name_lookup_url)
{
sNameLookupURL = name_lookup_url;
}
bool LLAvatarNameCache::hasNameLookupURL()
{
return !sNameLookupURL.empty();
}
void LLAvatarNameCache::idle()
{
// By convention, start running at first idle() call
sRunning = true;
// *TODO: Possibly re-enabled this based on People API load measurements
// 100 ms is the threshold for "user speed" operations, so we can
// stall for about that long to batch up requests.
//const F32 SECS_BETWEEN_REQUESTS = 0.1f;
//if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS))
//{
// 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())
{
return;
}
if (useDisplayNames())
{
requestNamesViaCapability();
}
else
{
// ...fall back to legacy name cache system
requestNamesViaLegacy();
}
}
bool LLAvatarNameCache::isRequestPending(const LLUUID& agent_id)
{
const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0;
F64 now = LLFrameTimer::getTotalSeconds();
F64 expire_time = now - PENDING_TIMEOUT_SECS;
pending_queue_t::const_iterator it = sPendingQueue.find(agent_id);
if (it != sPendingQueue.end())
{
bool request_expired = (it->second < expire_time);
return !request_expired;
}
return false;
}
void LLAvatarNameCache::eraseExpired()
{
F64 now = LLFrameTimer::getTotalSeconds();
cache_t::iterator it = sCache.begin();
while (it != sCache.end())
{
cache_t::iterator cur = it;
++it;
const LLAvatarName& av_name = cur->second;
if (av_name.mExpires < now)
{
sCache.erase(cur);
}
}
}
void LLAvatarNameCache::buildLegacyName(const std::string& full_name,
LLAvatarName* av_name)
{
llassert(av_name);
av_name->mUsername = "";
av_name->mDisplayName = full_name;
av_name->mIsDisplayNameDefault = true;
av_name->mIsDummy = true;
av_name->mExpires = F64_MAX;
}
// fills in av_name if it has it in the cache, even if expired (can check expiry time)
// returns bool specifying if av_name was filled, false otherwise
bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
{
if (sRunning)
{
// ...only do immediate lookups when cache is running
if (useDisplayNames())
{
// ...use display names cache
std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id);
if (it != sCache.end())
{
*av_name = it->second;
// re-request name if entry is expired
if (av_name->mExpires < LLFrameTimer::getTotalSeconds())
{
if (!isRequestPending(agent_id))
{
sAskQueue.insert(agent_id);
}
}
return true;
}
}
else
{
// ...use legacy names cache
std::string full_name;
if (gCacheName->getFullName(agent_id, full_name))
{
buildLegacyName(full_name, av_name);
return true;
}
}
}
if (!isRequestPending(agent_id))
{
sAskQueue.insert(agent_id);
}
return false;
}
void LLAvatarNameCache::fireSignal(const LLUUID& agent_id,
const callback_slot_t& slot,
const LLAvatarName& av_name)
{
callback_signal_t signal;
signal.connect(slot);
signal(agent_id, av_name);
}
void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot)
{
if (sRunning)
{
// ...only do immediate lookups when cache is running
if (useDisplayNames())
{
// ...use new cache
std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id);
if (it != sCache.end())
{
LLAvatarName& av_name = it->second;
if (av_name.mExpires > LLFrameTimer::getTotalSeconds())
{
av_name.mDisplayName = av_name.mDisplayName;
// ...name already exists in cache, fire callback now
fireSignal(agent_id, slot, av_name);
return;
}
}
}
else
{
// ...use old name system
std::string full_name;
if (gCacheName->getFullName(agent_id, full_name))
{
LLAvatarName av_name;
av_name.mDisplayName = av_name.mDisplayName;
buildLegacyName(full_name, &av_name);
fireSignal(agent_id, slot, av_name);
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);
}
}
void LLAvatarNameCache::setUseDisplayNames(bool use)
{
if (use != sUseDisplayNames)
{
sUseDisplayNames = use;
// flush our cache
sCache.clear();
mUseDisplayNamesSignal();
}
}
bool LLAvatarNameCache::useDisplayNames()
{
// Must be both manually set on and able to look up names.
return sUseDisplayNames && !sNameLookupURL.empty();
}
void LLAvatarNameCache::erase(const LLUUID& agent_id)
{
sCache.erase(agent_id);
}
void LLAvatarNameCache::fetch(const LLUUID& agent_id)
{
// re-request, even if request is already pending
sAskQueue.insert(agent_id);
}
void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_name)
{
// *TODO: update timestamp if zero?
sCache[agent_id] = av_name;
}
F64 LLAvatarNameCache::nameExpirationFromHeaders(LLSD headers)
{
F64 expires = 0.0;
if (expirationFromCacheControl(headers, &expires))
{
return expires;
}
else
{
// With no expiration info, default to an hour
const F64 DEFAULT_EXPIRES = 60.0 * 60.0;
F64 now = LLFrameTimer::getTotalSeconds();
return now + DEFAULT_EXPIRES;
}
}
bool LLAvatarNameCache::expirationFromCacheControl(LLSD headers, F64 *expires)
{
// Allow the header to override the default
LLSD cache_control_header = headers["cache-control"];
if (cache_control_header.isDefined())
{
S32 max_age = 0;
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;
}
}
return false;
}
void LLAvatarNameCache::addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb)
{
mUseDisplayNamesSignal.connect(cb);
}
static const std::string MAX_AGE("max-age");
static const boost::char_separator<char> EQUALS_SEPARATOR("=");
static const boost::char_separator<char> COMMA_SEPARATOR(",");
bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age)
{
// Split the string on "," to get a list of directives
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
tokenizer directives(cache_control, COMMA_SEPARATOR);
tokenizer::iterator token_it = directives.begin();
for ( ; token_it != directives.end(); ++token_it)
{
// Tokens may have leading or trailing whitespace
std::string token = *token_it;
LLStringUtil::trim(token);
if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
{
// ...this token starts with max-age, so let's chop it up by "="
tokenizer subtokens(token, EQUALS_SEPARATOR);
tokenizer::iterator subtoken_it = subtokens.begin();
// Must have a token
if (subtoken_it == subtokens.end()) return false;
std::string subtoken = *subtoken_it;
// Must exactly equal "max-age"
LLStringUtil::trim(subtoken);
if (subtoken != MAX_AGE) return false;
// Must have another token
++subtoken_it;
if (subtoken_it == subtokens.end()) return false;
subtoken = *subtoken_it;
// Must be a valid integer
// *NOTE: atoi() returns 0 for invalid values, so we have to
// check the string first.
// *TODO: Do servers ever send "0000" for zero? We don't handle it
LLStringUtil::trim(subtoken);
if (subtoken == "0")
{
*max_age = 0;
return true;
}
S32 val = atoi( subtoken.c_str() );
if (val > 0 && val < S32_MAX)
{
*max_age = val;
return true;
}
return false;
}
}
return false;
}

View File

@@ -0,0 +1,104 @@
/**
* @file llavatarnamecache.h
* @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names
* ("James Cook") from avatar UUIDs.
*
* $LicenseInfo:firstyear=2010&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$
*/
#ifndef LLAVATARNAMECACHE_H
#define LLAVATARNAMECACHE_H
#include "llavatarname.h" // for convenience
#include <boost/signals2.hpp>
class LLSD;
class LLUUID;
namespace LLAvatarNameCache
{
typedef boost::signals2::signal<void (void)> use_display_name_signal_t;
// Until the cache is set running, immediate lookups will fail and
// async lookups will be queued. This allows us to block requests
// until we know if the first region supports display names.
void initClass(bool running);
void cleanupClass();
void importFile(std::istream& istr);
void exportFile(std::ostream& ostr);
// On the viewer, usually a simulator capabilitity
// If empty, name cache will fall back to using legacy name
// lookup system
void setNameLookupURL(const std::string& name_lookup_url);
// Do we have a valid lookup URL, hence are we trying to use the
// new display name lookup system?
bool hasNameLookupURL();
// Periodically makes a batch request for display names not already in
// cache. Call once per frame.
void idle();
// If name is in cache, returns true and fills in provided LLAvatarName
// otherwise returns false
bool get(const LLUUID& agent_id, LLAvatarName *av_name);
// Callback types for get() below
typedef boost::signals2::signal<
void (const LLUUID& agent_id, const LLAvatarName& av_name)>
callback_signal_t;
typedef callback_signal_t::slot_type callback_slot_t;
// Fetches name information and calls callback.
// If name information is in cache, callback will be called immediately.
void get(const LLUUID& agent_id, callback_slot_t slot);
// Allow display names to be explicitly disabled for testing.
void setUseDisplayNames(bool use);
bool useDisplayNames();
bool isRequestPending(const LLUUID& agent_id);
void erase(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);
void insert(const LLUUID& agent_id, const LLAvatarName& av_name);
// Compute name expiration time from HTTP Cache-Control header,
// or return default value, in seconds from epoch.
F64 nameExpirationFromHeaders(LLSD headers);
void addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb);
}
// Parse a cache-control header to get the max-age delta-seconds.
// Returns true if header has max-age param and it parses correctly.
// Exported here to ease unit testing.
bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age);
#endif

View File

@@ -154,6 +154,8 @@ set(viewer_SOURCE_FILES
llfloateravatartextures.cpp
llfloaterbeacons.cpp
llfloaterblacklist.cpp
llviewerdisplayname.cpp
llfloaterdisplayname.cpp
llfloaterbuildoptions.cpp
llfloaterbulkpermission.cpp
llfloaterbump.cpp
@@ -612,7 +614,9 @@ set(viewer_HEADER_FILES
llfloateravatarpicker.h
llfloateravatartextures.h
llfloaterbeacons.h
llfloaterblacklist.h
llfloaterblacklist.h
llviewerdisplayname.h
llfloaterdisplayname.h
llfloaterbuildoptions.h
llfloaterbulkpermission.h
llfloaterbump.h

View File

@@ -12,6 +12,17 @@
<!-- !!! settings_ascent.xml -->
<key>PhoenixNameSystem</key>
<map>
<key>Comment</key>
<string>Use Display Names instead of Legacy Names. 0 = Old Style, 1 = Display Names and Username, 2 = Displayname only</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>0</integer>
</map>
<!-- Ascent-Specific Settings -->
<key>AscentPowerfulWizard</key>
<map>

View File

@@ -171,10 +171,7 @@
#include "llviewerthrottle.h"
#include "llparcel.h"
#include "llavatarnamecache.h"
#include "llinventoryview.h"
#include "llcommandlineparser.h"
@@ -1199,6 +1196,8 @@ bool LLAppViewer::cleanup()
LLPolyMesh::freeAllMeshes();
LLAvatarNameCache::cleanupClass();
delete gCacheName;
gCacheName = NULL;
@@ -3201,6 +3200,15 @@ void LLAppViewer::saveFinalSnapshot()
void LLAppViewer::loadNameCache()
{
// Phoenix: Wolfspirit: Loads the Display Name Cache. And set if we are using Display Names.
std::string filename =
gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml");
llifstream name_cache_stream(filename);
if(name_cache_stream.is_open())
{
LLAvatarNameCache::importFile(name_cache_stream);
}
if (!gCacheName) return;
std::string name_cache;
@@ -3223,6 +3231,15 @@ void LLAppViewer::loadNameCache()
void LLAppViewer::saveNameCache()
{
// Phoenix: Wolfspirit: Saves the Display Name Cache.
std::string filename =
gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml");
llofstream name_cache_stream(filename);
if(name_cache_stream.is_open())
{
LLAvatarNameCache::exportFile(name_cache_stream);
}
if (!gCacheName) return;
std::string name_cache;
@@ -3406,6 +3423,9 @@ void LLAppViewer::idle()
{
LLFastTimer t(LLFastTimer::FTM_NETWORK);
// Phoenix: Wolfspirit: Prepare the namecache.
idleNameCache();
////////////////////////////////////////////////
//
// Network processing
@@ -3739,6 +3759,61 @@ public:
};
void LLAppViewer::idleNameCache()
{
// Neither old nor new name cache can function before agent has a region
LLViewerRegion* region = gAgent.getRegion();
if (!region) return;
// deal with any queued name requests and replies.
gCacheName->processPending();
// Can't run the new cache until we have the list of capabilities
// for the agent region, and can therefore decide whether to use
// display names or fall back to the old name system.
if (!region->capabilitiesReceived()) return;
// Agent may have moved to a different region, so need to update cap URL
// for name lookups. Can't do this in the cap grant code, as caps are
// granted to neighbor regions before the main agent gets there. Can't
// do it in the move-into-region code because cap not guaranteed to be
// granted yet, for example on teleport.
bool had_capability = LLAvatarNameCache::hasNameLookupURL();
std::string name_lookup_url;
name_lookup_url.reserve(128); // avoid a memory allocation below
name_lookup_url = region->getCapability("GetDisplayNames");
bool have_capability = !name_lookup_url.empty();
if (have_capability)
{
// we have support for display names, use it
U32 url_size = name_lookup_url.size();
// capabilities require URLs with slashes before query params:
// https://<host>:<port>/cap/<uuid>/?ids=<blah>
// but the caps are granted like:
// https://<host>:<port>/cap/<uuid>
if (url_size > 0 && name_lookup_url[url_size-1] != '/')
{
name_lookup_url += '/';
}
LLAvatarNameCache::setNameLookupURL(name_lookup_url);
}
else
{
// Display names not available on this region
LLAvatarNameCache::setNameLookupURL( std::string() );
}
// Error recovery - did we change state?
if (had_capability != have_capability)
{
// name tags are persistant on screen, so make sure they refresh
//LLVOAvatar::invalidateNameTags();
}
// Phoenix: Wolfspirit: Check if we are using Display Names and set it. Then idle the cache.
LLAvatarNameCache::idle();
}
void LLAppViewer::sendLogoutRequest()
{

View File

@@ -195,6 +195,7 @@ private:
void idle();
void idleShutdown();
void idleNetwork();
void idleNameCache();
void sendLogoutRequest();
void disconnectViewer();

View File

@@ -35,6 +35,7 @@
#include "llviewerprecompiledheaders.h"
#include "llfloateravatarinfo.h"
#include "llavatarnamecache.h"
// viewer project includes
#include "llagentdata.h"
@@ -47,6 +48,7 @@
#include "lluuid.h"
#include "message.h"
const char FLOATER_TITLE[] = "Profile";
const LLRect FAI_RECT(0, 530, 420, 0);
@@ -122,7 +124,8 @@ LLFloaterAvatarInfo::LLFloaterAvatarInfo(const std::string& name, const LLRect &
}
gAvatarInfoInstances.addData(avatar_id, this); // must be done before callback below is called.
gCacheName->get(avatar_id, FALSE, callbackLoadAvatarName);
//gCacheName->get(avatar_id, FALSE, callbackLoadAvatarName);
LLAvatarNameCache::get(avatar_id, boost::bind(&LLFloaterAvatarInfo::callbackLoadAvatarName, _1, _2));
}
// virtual
@@ -242,10 +245,7 @@ void LLFloaterAvatarInfo::showProfileCallback(S32 option, void *userdata)
// static
void LLFloaterAvatarInfo::callbackLoadAvatarName(const LLUUID& id,
const std::string& first,
const std::string& last,
BOOL is_group,
void* data)
const LLAvatarName& av_name)
{
LLFloaterAvatarInfo *floater = gAvatarInfoInstances.getIfThere(id);
@@ -253,7 +253,8 @@ void LLFloaterAvatarInfo::callbackLoadAvatarName(const LLUUID& id,
{
// Build a new title including the avatar name.
std::ostringstream title;
title << first << " " << last << " - " << floater->getTitle();
//title << first << " " << last << " - " << floater->getTitle();
title << av_name.getCompleteName()<< " - " << floater->getTitle();
floater->setTitle(title.str());
}
}

View File

@@ -44,6 +44,7 @@
#include "lluuid.h"
#include "llpanelavatar.h"
class LLAvatarName;
class LLButton;
class LLCheckBoxCtrl;
class LLDropTarget;
@@ -91,8 +92,7 @@ public:
static LLFloaterAvatarInfo* getInstance(const LLUUID &id);
static void showProfileCallback(S32 option, void *userdata);
static void callbackLoadAvatarName(const LLUUID& id,
const std::string& first, const std::string& last, BOOL is_group, void* data);
static void callbackLoadAvatarName(const LLUUID& agent_id, const LLAvatarName& av_name);
void resetGroupList();
private:

View File

@@ -0,0 +1,232 @@
/**
* @file llfloaterdisplayname.cpp
* @author Leyla Farazha
* @brief Implementation of the LLFloaterDisplayName 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"
#include "llfloater.h"
#include "llnotifications.h"
#include "llviewerdisplayname.h"
#include "llnotify.h"
#include "llfloaterdisplayname.h"
#include "llavatarnamecache.h"
#include "lluictrlfactory.h"
#include "llagent.h"
LLFloaterDisplayName* LLFloaterDisplayName::sInstance = NULL;
LLFloaterDisplayName::LLFloaterDisplayName()
: LLFloater(std::string("Display Names Floater"))
{
LLFloaterDisplayName::sInstance = this;
}
// virtual
LLFloaterDisplayName::~LLFloaterDisplayName()
{
LLFloaterDisplayName::sInstance = NULL;
}
BOOL LLFloaterDisplayName::postBuild()
{
childSetAction("reset_btn", onReset, this);
childSetAction("cancel_btn", onCancel, this);
childSetAction("save_btn", onSave, this);
center();
return TRUE;
}
void LLFloaterDisplayName::show()
{
if (LLFloaterDisplayName::sInstance)
{
LLFloaterDisplayName::sInstance->open(); /*Flawfinder: ignore*/
return;
}
LLFloaterDisplayName *self = new LLFloaterDisplayName();
// Builds and adds to gFloaterView
LLUICtrlFactory::getInstance()->buildFloater(self, "floater_display_name.xml");
// Fix up rectangle
self->open(); /*Flawfinder: ignore*/
}
void LLFloaterDisplayName::onOpen()
{
getChild<LLUICtrl>("display_name_editor")->clear();
getChild<LLUICtrl>("display_name_confirm")->clear();
LLAvatarName av_name;
LLAvatarNameCache::get(gAgent.getID(), &av_name);
F64 now_secs = LLDate::now().secondsSinceEpoch();
if (now_secs < av_name.mNextUpdate)
{
// ...can't update until some time in the future
F64 next_update_local_secs =
av_name.mNextUpdate ;
LLDate next_update_local(next_update_local_secs);
// display as "July 18 12:17 PM"
// std::string next_update_string =
// next_update_local.toHTTPDateString("%B %d %I:%M %p");
std::string next_update_string = next_update_local.asString();
getChild<LLUICtrl>("lockout_text")->setTextArg("[TIME]", next_update_string);
getChild<LLUICtrl>("lockout_text")->setVisible(true);
getChild<LLUICtrl>("save_btn")->setEnabled(false);
getChild<LLUICtrl>("display_name_editor")->setEnabled(false);
getChild<LLUICtrl>("display_name_confirm")->setEnabled(false);
getChild<LLUICtrl>("cancel_btn")->setFocus(TRUE);
}
else
{
getChild<LLUICtrl>("lockout_text")->setVisible(false);
getChild<LLUICtrl>("save_btn")->setEnabled(true);
getChild<LLUICtrl>("display_name_editor")->setEnabled(true);
getChild<LLUICtrl>("display_name_confirm")->setEnabled(true);
}
}
void LLFloaterDisplayName::onCacheSetName(bool success,
const std::string& reason,
const LLSD& content)
{
if (success)
{
// Inform the user that the change took place, but will take a while
// to percolate.
LLSD args;
args["DISPLAY_NAME"] = content["display_name"];
LLNotifications::instance().add("SetDisplayNameSuccess", args);
// Re-fetch my name, as it may have been sanitized by the service
//LLAvatarNameCache::get(getAvatarId(),
// boost::bind(&LLPanelMyProfileEdit::onNameCache, this, _1, _2));
return;
}
// Request failed, notify the user
std::string error_tag = content["error_tag"].asString();
llinfos << "set name failure error_tag " << error_tag << llendl;
// We might have a localized string for this message
// error_args will usually be empty from the server.
if (!error_tag.empty()
&& LLNotifications::getInstance()->templateExists(error_tag))
{
LLNotifications::instance().add(error_tag);
return;
}
// The server error might have a localized message for us
std::string lang_code = LLUI::getLanguage();
LLSD error_desc = content["error_description"];
if (error_desc.has( lang_code ))
{
LLSD args;
args["MESSAGE"] = error_desc[lang_code].asString();
LLNotifications::instance().add("GenericAlert", args);
return;
}
// No specific error, throw a generic one
LLNotifications::instance().add("SetDisplayNameFailedGeneric");
}
void LLFloaterDisplayName::onCancel(void* data)
{
LLFloaterDisplayName* self = (LLFloaterDisplayName*)data;
self->setVisible(false);
}
void LLFloaterDisplayName::onReset(void* data)
{
LLFloaterDisplayName* self = (LLFloaterDisplayName*)data;
if (LLAvatarNameCache::useDisplayNames())
{
LLViewerDisplayName::set("",
boost::bind(&LLFloaterDisplayName::onCacheSetName, self, _1, _2, _3));
}
else
{
LLNotifications::instance().add("SetDisplayNameFailedGeneric");
}
self->setVisible(false);
}
void LLFloaterDisplayName::onSave(void* data)
{
LLFloaterDisplayName* self = (LLFloaterDisplayName*)data;
std::string display_name_utf8 = self->getChild<LLUICtrl>("display_name_editor")->getValue().asString();
std::string display_name_confirm = self->getChild<LLUICtrl>("display_name_confirm")->getValue().asString();
if (display_name_utf8.compare(display_name_confirm))
{
LLNotifications::instance().add("SetDisplayNameMismatch");
return;
}
const U32 DISPLAY_NAME_MAX_LENGTH = 31; // characters, not bytes
LLWString display_name_wstr = utf8string_to_wstring(display_name_utf8);
if (display_name_wstr.size() > DISPLAY_NAME_MAX_LENGTH)
{
LLSD args;
args["LENGTH"] = llformat("%d", DISPLAY_NAME_MAX_LENGTH);
LLNotifications::instance().add("SetDisplayNameFailedLength", args);
return;
}
if (LLAvatarNameCache::useDisplayNames())
{
LLViewerDisplayName::set(display_name_utf8,
boost::bind(&LLFloaterDisplayName::onCacheSetName, self, _1, _2, _3));
}
else
{
LLNotifications::instance().add("SetDisplayNameFailedGeneric");
}
self->setVisible(false);
}

View File

@@ -0,0 +1,52 @@
/**
* @file llfloaterdisplayname.h
*
* $LicenseInfo:firstyear=2009&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$
*/
#ifndef LLFLOATERDISPLAYNAME_H
#define LLFLOATERDISPLAYNAME_H
class LLFloaterDisplayName : public LLFloater
{
public:
LLFloaterDisplayName();
virtual ~LLFloaterDisplayName();
/*virtual*/ BOOL postBuild();
static void onSave(void* data);
static void onReset(void* data);
static void onCancel(void* data);
static void show();
/*virtual*/ void onOpen();
private:
void onCacheSetName(bool success,
const std::string& reason,
const LLSD& content);
protected:
static LLFloaterDisplayName* sInstance;
};
#endif

View File

@@ -34,6 +34,8 @@
#include "llnameeditor.h"
#include "llcachename.h"
#include "llavatarname.h"
#include "llavatarnamecache.h"
#include "llagent.h"
#include "llfontgl.h"
@@ -82,6 +84,11 @@ LLNameEditor::~LLNameEditor()
LLNameEditor::sInstances.erase(this);
}
void LLNameEditor::on_avatar_name_response(const LLUUID& agent_id, const LLAvatarName& av_name, void *userdata){
LLNameEditor* self = (LLNameEditor*)userdata;
if(self->mNameID == agent_id) self->setText(av_name.getCompleteName());
}
void LLNameEditor::setNameID(const LLUUID& name_id, BOOL is_group)
{
mNameID = name_id;
@@ -90,7 +97,21 @@ void LLNameEditor::setNameID(const LLUUID& name_id, BOOL is_group)
if (!is_group)
{
gCacheName->getFullName(name_id, name);
/* Phoenix: Wolfspirit: Check if we already have the name in Cache.
This will (if DN is enabled) return the full name (Displayname (username)).
If DN is diabled it will return the old "Firstname Lastname" style.
If it is not in cache, then add the LegacyName until we received the name from the callback.
Do the Request only, if DN is enabled. */
LLAvatarName av_name;
if(LLAvatarNameCache::get(name_id, &av_name)){
name = av_name.getCompleteName();
}
else
{
gCacheName->getFullName(name_id,name);
if(LLAvatarNameCache::useDisplayNames()) LLAvatarNameCache::get(name_id, boost::bind(&LLNameEditor::on_avatar_name_response, _1, _2, this));
}
}
else
{
@@ -108,7 +129,16 @@ void LLNameEditor::refresh(const LLUUID& id, const std::string& firstname,
std::string name;
if (!is_group)
{
name = firstname + " " + lastname;
/* Phoenix: Wolfspirit: Use DN Cache first */
LLAvatarName av_name;
if(LLAvatarNameCache::get(id, &av_name)){
name = av_name.getCompleteName();
}
else
{
gCacheName->getFullName(id,name);
if(LLAvatarNameCache::useDisplayNames()) LLAvatarNameCache::get(id, boost::bind(&LLNameEditor::on_avatar_name_response, _1, _2, this));
}
}
else
{

View File

@@ -40,6 +40,7 @@
#include "llstring.h"
#include "llfontgl.h"
#include "lllineeditor.h"
#include "llavatarname.h"
class LLNameEditor
@@ -79,6 +80,7 @@ public:
private:
static std::set<LLNameEditor*> sInstances;
static void on_avatar_name_response(const LLUUID& agent_id, const LLAvatarName& av_name, void *userdata);
private:
LLUUID mNameID;

View File

@@ -83,15 +83,13 @@
#include "roles_constants.h"
#include "lluictrlfactory.h"
#include "llviewermenu.h"
#include "llavatarnamecache.h"
#include <iosfwd>
// [RLVa:KB]
#include "rlvhandler.h"
// [/RLVa:KB]
@@ -1416,9 +1414,11 @@ void LLPanelAvatar::setAvatar(LLViewerObject *avatarp)
{
name.assign("");
}
LLAvatarName av_name;
LLAvatarNameCache::get(avatarp->getID(), &av_name);
// If we have an avatar pointer, they must be online.
setAvatarID(avatarp->getID(), name, ONLINE_STATUS_YES);
setAvatarID(avatarp->getID(), av_name.getCompleteName(), ONLINE_STATUS_YES);
}
void LLPanelAvatar::onCommitKey(LLUICtrl* ctrl, void* data)

View File

@@ -44,6 +44,8 @@
#include "llagent.h"
#include "llviewerregion.h"
#include "llavatarnamecache.h"
#include "llvoavatar.h"
LLPanelGeneral::LLPanelGeneral()
{
@@ -55,6 +57,9 @@ BOOL LLPanelGeneral::postBuild()
LLComboBox* fade_out_combobox = getChild<LLComboBox>("fade_out_combobox");
fade_out_combobox->setCurrentByIndex(gSavedSettings.getS32("RenderName"));
LLComboBox* namesystem_combobox = getChild<LLComboBox>("namesystem_combobox");
namesystem_combobox->setCurrentByIndex(gSavedSettings.getS32("PhoenixNameSystem"));
childSetValue("default_start_location", gSavedSettings.getBOOL("LoginLastLocation") ? "MyLastLocation" : "MyHome");
childSetValue("show_location_checkbox", gSavedSettings.getBOOL("ShowStartLocation"));
childSetValue("show_all_title_checkbox", gSavedSettings.getBOOL("RenderHideGroupTitleAll"));
@@ -117,6 +122,16 @@ void LLPanelGeneral::apply()
LLComboBox* fade_out_combobox = getChild<LLComboBox>("fade_out_combobox");
gSavedSettings.setS32("RenderName", fade_out_combobox->getCurrentIndex());
LLComboBox* namesystem_combobox = getChild<LLComboBox>("namesystem_combobox");
if(gSavedSettings.getS32("PhoenixNameSystem")!=namesystem_combobox->getCurrentIndex()){
gSavedSettings.setS32("PhoenixNameSystem", namesystem_combobox->getCurrentIndex());
if(gAgent.getRegion()){
if(namesystem_combobox->getCurrentIndex()<=0 || namesystem_combobox->getCurrentIndex()>2) LLAvatarNameCache::setUseDisplayNames(false);
else LLAvatarNameCache::setUseDisplayNames(true);
//LLVOAvatar::invalidateNameTags(); No need, they'll be updated on the next loop
}
}
gSavedSettings.setBOOL("LoginLastLocation", childGetValue("default_start_location").asString() == "MyLastLocation");
gSavedSettings.setBOOL("ShowStartLocation", childGetValue("show_location_checkbox"));
gSavedSettings.setBOOL("RenderHideGroupTitleAll", childGetValue("show_all_title_checkbox"));

View File

@@ -205,6 +205,8 @@
#include "scriptcounter.h"
// </edit>
#include "llavatarnamecache.h"
// [RLVa:KB]
#include "rlvhandler.h"
// [/RLVa:KB]
@@ -2700,6 +2702,13 @@ bool idle_startup()
LLAppViewer::instance()->loadNameCache();
}
// Start cache in not-running state until we figure out if we have
// capabilities for display name lookup
LLAvatarNameCache::initClass(false);
S32 phoenix_name_system = gSavedSettings.getS32("PhoenixNameSystem");
if(phoenix_name_system <= 0 || phoenix_name_system > 2) LLAvatarNameCache::setUseDisplayNames(false);
else LLAvatarNameCache::setUseDisplayNames(true);
// *Note: this is where gWorldMap used to be initialized.
// register null callbacks for audio until the audio system is initialized

View File

@@ -0,0 +1,212 @@
/**
* @file llviewerdisplayname.cpp
* @brief Wrapper for display name functionality
*
* $LicenseInfo:firstyear=2010&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$
*/
/*
Ported to Phoenix by Wolfspirit Magic.
*/
#include "llviewerprecompiledheaders.h"
#include "llviewerdisplayname.h"
// viewer includes
#include "llagent.h"
#include "llviewerregion.h"
#include "llvoavatar.h"
// library includes
#include "llavatarnamecache.h"
#include "llhttpclient.h"
#include "llhttpnode.h"
#include "llnotifications.h"
#include "llui.h" // getLanguage()
namespace LLViewerDisplayName
{
// Fired when viewer receives server response to display name change
set_name_signal_t sSetDisplayNameSignal;
// Fired when there is a change in the agent's name
name_changed_signal_t sNameChangedSignal;
void addNameChangedCallback(const name_changed_signal_t::slot_type& cb)
{
sNameChangedSignal.connect(cb);
}
}
class LLSetDisplayNameResponder : public LLHTTPClient::Responder
{
public:
// only care about errors
/*virtual*/ void error(U32 status, const std::string& reason)
{
LLViewerDisplayName::sSetDisplayNameSignal(false, "", LLSD());
LLViewerDisplayName::sSetDisplayNameSignal.disconnect_all_slots();
}
};
void LLViewerDisplayName::set(const std::string& display_name, const set_name_slot_t& slot)
{
// TODO: simple validation here
LLViewerRegion* region = gAgent.getRegion();
llassert(region);
std::string cap_url = region->getCapability("SetDisplayName");
if (cap_url.empty())
{
// this server does not support display names, report error
slot(false, "unsupported", LLSD());
return;
}
// People API can return localized error messages. Indicate our
// language preference via header.
LLSD headers;
headers["Accept-Language"] = LLUI::getLanguage();
// People API requires both the old and new value to change a variable.
// Our display name will be in cache before the viewer's UI is available
// to request a change, so we can use direct lookup without callback.
LLAvatarName av_name;
if (!LLAvatarNameCache::get( gAgent.getID(), &av_name))
{
slot(false, "name unavailable", LLSD());
return;
}
// People API expects array of [ "old value", "new value" ]
LLSD change_array = LLSD::emptyArray();
change_array.append(av_name.mDisplayName);
change_array.append(display_name);
llinfos << "Set name POST to " << cap_url << llendl;
// Record our caller for when the server sends back a reply
sSetDisplayNameSignal.connect(slot);
// POST the requested change. The sim will not send a response back to
// this request directly, rather it will send a separate message after it
// communicates with the back-end.
LLSD body;
body["display_name"] = change_array;
LLHTTPClient::post(cap_url, body, new LLSetDisplayNameResponder, headers);
}
class LLSetDisplayNameReply : public LLHTTPNode
{
LOG_CLASS(LLSetDisplayNameReply);
public:
/*virtual*/ void post(
LLHTTPNode::ResponsePtr response,
const LLSD& context,
const LLSD& input) const
{
LLSD body = input["body"];
S32 status = body["status"].asInteger();
bool success = (status == 200);
std::string reason = body["reason"].asString();
LLSD content = body["content"];
llinfos << "status " << status << " reason " << reason << llendl;
// If viewer's concept of display name is out-of-date, the set request
// will fail with 409 Conflict. If that happens, fetch up-to-date
// name information.
if (status == 409)
{
LLUUID agent_id = gAgent.getID();
// Flush stale data
LLAvatarNameCache::erase( agent_id );
// Queue request for new data
LLAvatarName ignored;
LLAvatarNameCache::get( agent_id, &ignored );
// Kill name tag, as it is wrong
LLVOAvatar::invalidateNameTag( agent_id );
}
// inform caller of result
LLViewerDisplayName::sSetDisplayNameSignal(success, reason, content);
LLViewerDisplayName::sSetDisplayNameSignal.disconnect_all_slots();
}
};
class LLDisplayNameUpdate : public LLHTTPNode
{
/*virtual*/ void post(
LLHTTPNode::ResponsePtr response,
const LLSD& context,
const LLSD& input) const
{
LLSD body = input["body"];
LLUUID agent_id = body["agent_id"];
std::string old_display_name = body["old_display_name"];
// By convention this record is called "agent" in the People API
LLSD name_data = body["agent"];
// Inject the new name data into cache
LLAvatarName av_name;
av_name.fromLLSD( name_data );
llinfos << "name-update now " << LLDate::now()
<< " next_update " << LLDate(av_name.mNextUpdate)
<< llendl;
// Name expiration time may be provided in headers, or we may use a
// default value
// *TODO: get actual headers out of ResponsePtr
//LLSD headers = response->mHeaders;
LLSD headers;
av_name.mExpires =
LLAvatarNameCache::nameExpirationFromHeaders(headers);
LLAvatarNameCache::insert(agent_id, av_name);
// force name tag to update
LLVOAvatar::invalidateNameTag(agent_id);
LLSD args;
args["OLD_NAME"] = old_display_name;
args["SLID"] = av_name.mUsername;
args["NEW_NAME"] = av_name.mDisplayName;
LLNotifications::instance().add("DisplayNameUpdate", args);
if (agent_id == gAgent.getID())
{
LLViewerDisplayName::sNameChangedSignal();
}
}
};
LLHTTPRegistration<LLSetDisplayNameReply>
gHTTPRegistrationMessageSetDisplayNameReply(
"/message/SetDisplayNameReply");
LLHTTPRegistration<LLDisplayNameUpdate>
gHTTPRegistrationMessageDisplayNameUpdate(
"/message/DisplayNameUpdate");

View File

@@ -0,0 +1,55 @@
/**
* @file llviewerdisplayname.h
* @brief Wrapper for display name functionality
*
* $LicenseInfo:firstyear=2010&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$
*/
/*
Ported to Phoenix by Wolfspirit Magic.
*/
#ifndef LLVIEWERDISPLAYNAME_H
#define LLVIEWERDISPLAYNAME_H
#include <boost/signals2.hpp>
class LLSD;
class LLUUID;
namespace LLViewerDisplayName
{
typedef boost::signals2::signal<
void (bool success, const std::string& reason, const LLSD& content)>
set_name_signal_t;
typedef set_name_signal_t::slot_type set_name_slot_t;
typedef boost::signals2::signal<void (void)> name_changed_signal_t;
typedef name_changed_signal_t::slot_type name_changed_slot_t;
// Sends an update to the server to change a display name
// and call back when done. May not succeed due to service
// unavailable or name not available.
void set(const std::string& display_name, const set_name_slot_t& slot);
void addNameChangedCallback(const name_changed_signal_t::slot_type& cb);
}
#endif // LLVIEWERDISPLAYNAME_H

View File

@@ -250,6 +250,7 @@
// </edit>
#include "scriptcounter.h"
#include "llfloaterdisplayname.h"
using namespace LLVOAvatarDefines;
void init_client_menu(LLMenuGL* menu);
@@ -6444,6 +6445,11 @@ class LLShowFloater : public view_listener_t
gAgent.changeCameraToCustomizeAvatar();
}
}
// Phoenix: Wolfspirit: Enabled Show Floater out of viewer menu
else if (floater_name == "displayname")
{
LLFloaterDisplayName::show();
}
else if (floater_name == "friends")
{
LLFloaterMyFriends::toggleInstance(0);

View File

@@ -126,6 +126,8 @@ public:
mRegion->showReleaseNotes();
}
}
mRegion->setCapabilitiesReceived(true);
if (STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState())
{
@@ -1455,6 +1457,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
capabilityNames.append("FetchLibDescendents");
capabilityNames.append("GetTexture");
capabilityNames.append("GroupProposalBallot");
capabilityNames.append("GetDisplayNames");
capabilityNames.append("HomeLocation");
capabilityNames.append("MapLayer");
capabilityNames.append("MapLayerGod");
@@ -1472,6 +1475,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
capabilityNames.append("SendPostcard");
capabilityNames.append("SendUserReport");
capabilityNames.append("SendUserReportWithScreenshot");
capabilityNames.append("SetDisplayName");
capabilityNames.append("ServerReleaseNotes");
capabilityNames.append("StartGroupProposal");
capabilityNames.append("TextureStats");
@@ -1533,6 +1537,16 @@ std::string LLViewerRegion::getCapability(const std::string& name) const
return iter->second;
}
bool LLViewerRegion::capabilitiesReceived() const
{
return mCapabilitiesReceived;
}
void LLViewerRegion::setCapabilitiesReceived(bool received)
{
mCapabilitiesReceived = received;
}
void LLViewerRegion::logActiveCapabilities() const
{
int count = 0;

View File

@@ -230,6 +230,9 @@ public:
std::string getCapability(const std::string& name) const;
static bool isSpecialCapabilityName(const std::string &name);
void logActiveCapabilities() const;
// has region received its final (not seed) capability list?
bool capabilitiesReceived() const;
void setCapabilitiesReceived(bool received);
const LLHost &getHost() const { return mHost; }
const U64 &getHandle() const { return mHandle; }
@@ -397,6 +400,7 @@ private:
private:
bool mAlive; // can become false if circuit disconnects
bool mCapabilitiesReceived;
//spatial partitions for objects in this region
std::vector<LLSpatialPartition*> mObjectPartition;

View File

@@ -97,6 +97,9 @@
#include "llimagemetadatareader.h"
// </edit>
#include "llavatarname.h"
#include "llavatarnamecache.h"
// [RLVa:KB]
#include "rlvhandler.h"
// [/RLVa:KB]
@@ -758,6 +761,9 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mAppearanceAnimating(FALSE),
mNameString(),
mTitle(),
mRenderedName(),
mUsedNameSystem(),
mClientName(),
mNameAway(FALSE),
mNameBusy(FALSE),
mNameMute(FALSE),
@@ -3513,6 +3519,8 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
mVisibleChat = visible_chat;
new_name = TRUE;
}
static LLCachedControl<S32> phoenix_name_system("PhoenixNameSystem", 0);
// [RLVa:KB] - Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.0b
if (fRlvShowNames)
@@ -3695,6 +3703,27 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
if (mNameText.notNull() && firstname && lastname)
{
/*
Phoenix: Wolfspirit:
The following part replaces the username with the Displayname, if Displaynames are enabled
*/
LLAvatarName av_name;
bool dnhasloaded = false;
bool useddn = true;
if(LLAvatarNameCache::useDisplayNames() && LLAvatarNameCache::get(getID(), &av_name)) dnhasloaded=true;
std::string usedname;
if(dnhasloaded && !av_name.mIsDisplayNameDefault && !av_name.mIsDummy
&& av_name.mDisplayName != av_name.getLegacyName()) usedname = av_name.mDisplayName;
else {
usedname = firstname->getString();
usedname += " ";
usedname += lastname->getString();
dnhasloaded=false;
useddn=false;
}
BOOL is_away = mSignaledAnimations.find(ANIM_AGENT_AWAY) != mSignaledAnimations.end();
if(mNameAway && ! is_away) mIdleTimer.reset();
BOOL is_busy = mSignaledAnimations.find(ANIM_AGENT_BUSY) != mSignaledAnimations.end();
@@ -3713,11 +3742,12 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
if (mNameString.empty() ||
new_name ||
mRenderedName != usedname ||
mUsedNameSystem != phoenix_name_system ||
(!title && !mTitle.empty()) ||
(title && mTitle != title->getString()) ||
(is_away != mNameAway || is_busy != mNameBusy || is_muted != mNameMute)
|| is_appearance != mNameAppearance
|| client.length() ) // <edit/>
|| is_appearance != mNameAppearance || client != mClientName)
{
std::string line;
@@ -3729,23 +3759,42 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
{
// If all group titles are turned off, stack first name
// on a line above last name
line += firstname->getString();
line += "\n";
if(!dnhasloaded){
line += firstname->getString();
line += "\n";
line += lastname->getString();
}
else
{
line += usedname;
}
}
else if (title && title->getString() && title->getString()[0] != '\0')
{
line += title->getString();
LLStringFn::replace_ascii_controlchars(line,LL_UNKNOWN_CHAR);
line += "\n";
line += firstname->getString();
if(!dnhasloaded){
line += usedname;
}
else
{
useddn=true;
line += usedname;
}
}
else
{
line += firstname->getString();
if(!dnhasloaded){
line += usedname;
}
else
{
useddn=true;
line += usedname;
}
}
line += " ";
line += lastname->getString();
// [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.0b
}
else
@@ -3799,6 +3848,19 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
line += " (" + additions + ")";
}
if(useddn){
if(phoenix_name_system != 2){
line += "\n";
line += "("+av_name.mUsername+")";
}
mRenderedName = av_name.mDisplayName;
}
else
{
mRenderedName = firstname->getString();
mRenderedName += " ";
mRenderedName += lastname->getString();
}
if (is_appearance)
{
line += "\n";
@@ -3813,6 +3875,8 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
mNameAway = is_away;
mNameBusy = is_busy;
mNameMute = is_muted;
mClientName = client;
mUsedNameSystem = phoenix_name_system;
mNameAppearance = is_appearance;
mTitle = title ? title->getString() : "";
LLStringFn::replace_ascii_controlchars(mTitle,LL_UNKNOWN_CHAR);
@@ -3926,6 +3990,44 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
}
}
/* Phoenix: Wolfspirit: This allows us to replace one specific nametag of a user */
void LLVOAvatar::clearNameTag()
{
mNameString.clear();
if (mNameText)
{
mNameText->setLabel("");
mNameText->setString(mNameString);
}
}
//static
void LLVOAvatar::invalidateNameTag(const LLUUID& agent_id)
{
LLViewerObject* obj = gObjectList.findObject(agent_id);
if (!obj) return;
LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(obj);
if (!avatar) return;
avatar->clearNameTag();
}
//staticmNameString.empty()
void LLVOAvatar::invalidateNameTags()
{
for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
iter != LLCharacter::sInstances.end(); ++iter)
{
LLVOAvatar* avatar = (LLVOAvatar*) *iter;
if (!avatar) continue;
if (avatar->isDead()) continue;
avatar->clearNameTag();
}
}
void LLVOAvatar::idleUpdateTractorBeam()
{

View File

@@ -50,6 +50,7 @@
#include "llwearable.h"
#include "llvoavatardefines.h"
#include "emeraldboobutils.h"
#include "llavatarname.h"
extern const LLUUID ANIM_AGENT_BODY_NOISE;
@@ -119,6 +120,10 @@ public:
void idleUpdateWindEffect();
void idleUpdateBoobEffect();
void idleUpdateNameTag(const LLVector3& root_pos_last);
void clearNameTag();
static void invalidateNameTag(const LLUUID& agent_id);
// force all name tags to rebuild, useful when display names turned on/off
static void invalidateNameTags();
void idleUpdateRenderCost();
void idleUpdateTractorBeam();
void idleUpdateBelowWater();
@@ -505,6 +510,7 @@ private:
std::deque<LLChat> mChats;
BOOL mTyping;
LLFrameTimer mTypingTimer;
static void on_avatar_name_response(const LLUUID& agent_id, const LLAvatarName& av_name, void *userdata);
//--------------------------------------------------------------------
// wind rippling in clothes
@@ -725,6 +731,9 @@ private:
BOOL mRenderTag;
BOOL mVisibleChat;
BOOL mRenderGroupTitles;
std::string mRenderedName;
std::string mClientName;
S32 mUsedNameSystem;
std::string mDebugText;
U64 mLastRegionHandle;

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater can_close="true" can_drag_on_left="false" can_minimize="false"
can_resize="false" enabled="true" height="220"
mouse_opaque="true" name="Display Name" title="CHANGE DISPLAY NAME" width="400">
<text length="1" bottom_delta="-50" font="SansSerif" height="25" left="20" text_color="AlertTextColor" name="lockout_text" visible="true" width="360"
wrap="true">
Next possible change: [TIME].
</text>
<text left="20" bottom_delta="-20" width="360" height="18" font="SansSerif" name="set_name_label">
New Display Name:
</text>
<line_editor max_length="31" name="display_name_editor" width="300" height="20" left="50" bottom_delta="-20" />
<text left="20" bottom_delta="-25" width="360" height="18" font="SansSerif" name="name_confirm_label">
Type your new name again to confirm:
</text>
<line_editor max_length="31" width="300" name="display_name_confirm" height="20" bottom_delta="-20" left="50" />
<button height="20" bottom_delta="-28" label="Save" font="SansSerif" left="20" name="save_btn" tool_tip="Save your new Display Name" width="113" />
<button height="20" bottom_delta="0" label="Reset" font="SansSerif" left_delta="123" name="reset_btn" tool_tip="Make Display Name the same as Username" width="113" />
<button height="20" bottom_delta="0" label="Cancel" font="SansSerif" left_delta="123" name="cancel_btn" tool_tip="Cancel and close this Dialog" width="113" />
<text left="20" bottom_delta="-25" width="360" height="18" font="SansSerif" name="name_confirm_label">
The name you give your avatar is called yours Display Name.
</text>
<text left="20" bottom_delta="-18" width="360" height="18" font="SansSerif" name="name_confirm_label">
You can change it once a week.
</text>
</floater>

View File

@@ -219,6 +219,10 @@
<on_click function="ShowFloater" userdata="appearance" />
<on_enable function="Edit.EnableCustomizeAvatar" />
</menu_item_call>
<menu_item_call bottom="-362" enabled="true" height="19" label="Display Name..." left="0"
mouse_opaque="true" name="Display Name..." width="153">
<on_click function="ShowFloater" userdata="displayname" />
</menu_item_call>
<menu_item_separator bottom="-370" enabled="true" height="8" label="-----------" left="0"
mouse_opaque="true" name="separator7" width="153" />
<menu_item_check bottom="-389" enabled="true" height="19" label="Friends..." left="0"

View File

@@ -6688,5 +6688,88 @@ Yes
<global name="PermNo">
No
</global>
<notification
icon="alertmodal.tga"
name="SetDisplayNameSuccess"
type="alert">
Hi [DISPLAY_NAME]!
Just like in real life, it takes a while for everyone to learn about a new name. Please allow several days for [http://wiki.secondlife.com/wiki/Setting_your_display_name your name to update] in objects, scripts, search, etc.
</notification>
<notification
icon="alertmodal.tga"
name="SetDisplayNameBlocked"
type="alert">
Sorry, you cannot change your display name. If you feel this is in error, please contact support.
</notification>
<notification
icon="alertmodal.tga"
name="SetDisplayNameFailedLength"
type="alertmodal">
Sorry, that name is too long. Display names can have a maximum of [LENGTH] characters.
Please try a shorter name.
</notification>
<notification
icon="alertmodal.tga"
name="SetDisplayNameFailedGeneric"
type="alertmodal">
Sorry, we could not set your display name. Please try again later.
</notification>
<notification
icon="alertmodal.tga"
name="SetDisplayNameMismatch"
type="alertmodal">
The display names you entered do not match. Please re-enter.
</notification>
<!-- *NOTE: This should never happen -->
<notification
icon="alertmodal.tga"
name="AgentDisplayNameUpdateThresholdExceeded"
type="alertmodal">
Sorry, you have to wait longer before you can change your display name.
See http://wiki.secondlife.com/wiki/Setting_your_display_name
Please try again later.
</notification>
<notification
icon="alertmodal.tga"
name="AgentDisplayNameSetBlocked"
type="alertmodal">
Sorry, we could not set your requested name because it contains a banned word.
Please try a different name.
</notification>
<notification
icon="alertmodal.tga"
name="AgentDisplayNameSetInvalidUnicode"
type="alertmodal">
The display name you wish to set contains invalid characters.
</notification>
<notification
icon="alertmodal.tga"
name="AgentDisplayNameSetOnlyPunctuation"
type="alertmodal">
Your display name must contain letters other than punctuation.
</notification>
<notification
icon="notifytip.tga"
name="DisplayNameUpdate"
type="notifytip">
[OLD_NAME] ([SLID]) is now known as [NEW_NAME].
</notification>
</notifications>

View File

@@ -29,6 +29,18 @@
Always
</combo_item>
</combo_box>
<combo_box bottom_delta="0" follows="left|top" height="18" left="330"
mouse_opaque="true" name="namesystem_combobox" width="146">
<combo_item name="Old Names" value="Old Names">
Old Names
</combo_item>
<combo_item name="Display Names (with Username)" value="Display Names (with Username)">
Display Names (with Username)
</combo_item>
<combo_item name="Display Names only" value="Display Names only">
Display Names only
</combo_item>
</combo_box>
<check_box bottom_delta="-25" follows="left|top"
font="SansSerifSmall" height="16" initial_value="true"
label="Small Avatar Names" left="151" name="small_avatar_names_checkbox"