From 6866a36a5c80e4bc02f60eecc34243c41e146bcc Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Sat, 20 Nov 2010 06:01:09 +0100 Subject: [PATCH] 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. --- etc/message.xml | 16 + indra/llcommon/CMakeLists.txt | 2 + indra/llcommon/llavatarname.cpp | 117 +++ indra/llcommon/llavatarname.h | 95 ++ indra/llmessage/CMakeLists.txt | 2 + indra/llmessage/llavatarnamecache.cpp | 826 ++++++++++++++++++ indra/llmessage/llavatarnamecache.h | 104 +++ indra/newview/CMakeLists.txt | 6 +- indra/newview/app_settings/settings.xml | 11 + indra/newview/llappviewer.cpp | 83 +- indra/newview/llappviewer.h | 1 + indra/newview/llfloateravatarinfo.cpp | 13 +- indra/newview/llfloateravatarinfo.h | 4 +- indra/newview/llfloaterdisplayname.cpp | 232 +++++ indra/newview/llfloaterdisplayname.h | 52 ++ indra/newview/llnameeditor.cpp | 34 +- indra/newview/llnameeditor.h | 2 + indra/newview/llpanelavatar.cpp | 8 +- indra/newview/llpanelgeneral.cpp | 15 + indra/newview/llstartup.cpp | 9 + indra/newview/llviewerdisplayname.cpp | 212 +++++ indra/newview/llviewerdisplayname.h | 55 ++ indra/newview/llviewermenu.cpp | 6 + indra/newview/llviewerregion.cpp | 14 + indra/newview/llviewerregion.h | 4 + indra/newview/llvoavatar.cpp | 118 ++- indra/newview/llvoavatar.h | 9 + .../xui/en-us/floater_display_name.xml | 29 + .../skins/default/xui/en-us/menu_viewer.xml | 4 + .../skins/default/xui/en-us/notifications.xml | 83 ++ .../xui/en-us/panel_preferences_general.xml | 12 + 31 files changed, 2151 insertions(+), 27 deletions(-) create mode 100644 indra/llcommon/llavatarname.cpp create mode 100644 indra/llcommon/llavatarname.h create mode 100644 indra/llmessage/llavatarnamecache.cpp create mode 100644 indra/llmessage/llavatarnamecache.h create mode 100644 indra/newview/llfloaterdisplayname.cpp create mode 100644 indra/newview/llfloaterdisplayname.h create mode 100644 indra/newview/llviewerdisplayname.cpp create mode 100644 indra/newview/llviewerdisplayname.h create mode 100644 indra/newview/skins/default/xui/en-us/floater_display_name.xml diff --git a/etc/message.xml b/etc/message.xml index e09967917..847f483aa 100644 --- a/etc/message.xml +++ b/etc/message.xml @@ -572,6 +572,22 @@ false + + DisplayNameUpdate + + flavor + llsd + trusted-sender + true + + + SetDisplayNameReply + + flavor + llsd + trusted-sender + true + capBans diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 1b830258f..a472649a4 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -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 diff --git a/indra/llcommon/llavatarname.cpp b/indra/llcommon/llavatarname.cpp new file mode 100644 index 000000000..3e3d3502b --- /dev/null +++ b/indra/llcommon/llavatarname.cpp @@ -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; +} diff --git a/indra/llcommon/llavatarname.h b/indra/llcommon/llavatarname.h new file mode 100644 index 000000000..30c4dced6 --- /dev/null +++ b/indra/llcommon/llavatarname.h @@ -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 + +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 diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 65aec5ed7..056fddf71 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -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 diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp new file mode 100644 index 000000000..8be98ec30 --- /dev/null +++ b/indra/llmessage/llavatarnamecache.cpp @@ -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 + +#include +#include + +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 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 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 signal_map_t; + signal_map_t sSignalMap; + + // names we know about + typedef std::map 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: + + + + agents + + + display_name_next_update + 2010-04-16T21:34:02+00:00Z + display_name_expires + 2010-04-16T21:32:26.142178+00:00Z + display_name + MickBot390 LLQABot + sl_id + mickbot390.llqabot + id + 0012809d-7d2d-4c24-9609-af1230a37715 + is_display_name_default + false + + + display_name_next_update + 2010-04-16T21:34:02+00:00Z + display_name_expires + 2010-04-16T21:32:26.142178+00:00Z + display_name + Bjork Gudmundsdottir + sl_id + sardonyx.linden + id + 3941037e-78ab-45f0-b421-bd6e77c1804d + is_display_name_default + true + + + + +*/ + +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 mAgentIDs; + + // Need the headers to look up Expires: and Retry-After: + LLSD mHeaders; + +public: + LLAvatarNameResponder(const std::vector& 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::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 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::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::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 EQUALS_SEPARATOR("="); +static const boost::char_separator 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 > 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; +} + diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h new file mode 100644 index 000000000..3cb601951 --- /dev/null +++ b/indra/llmessage/llavatarnamecache.h @@ -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 + +class LLSD; +class LLUUID; + +namespace LLAvatarNameCache +{ + + typedef boost::signals2::signal 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 diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 01db449b7..43318316a 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -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 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index b4a37e0a4..e086ff6b3 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -12,6 +12,17 @@ + PhoenixNameSystem + + Comment + Use Display Names instead of Legacy Names. 0 = Old Style, 1 = Display Names and Username, 2 = Displayname only + Persist + 1 + Type + S32 + Value + 0 + AscentPowerfulWizard diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 08f321760..bf2e07330 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -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://:/cap//?ids= + // but the caps are granted like: + // https://:/cap/ + 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() { diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index b0563e984..d7bde2bfe 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -195,6 +195,7 @@ private: void idle(); void idleShutdown(); void idleNetwork(); + void idleNameCache(); void sendLogoutRequest(); void disconnectViewer(); diff --git a/indra/newview/llfloateravatarinfo.cpp b/indra/newview/llfloateravatarinfo.cpp index 06188750c..d3fc5596d 100644 --- a/indra/newview/llfloateravatarinfo.cpp +++ b/indra/newview/llfloateravatarinfo.cpp @@ -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()); } } diff --git a/indra/newview/llfloateravatarinfo.h b/indra/newview/llfloateravatarinfo.h index 1cc17d1fa..b751aeda9 100644 --- a/indra/newview/llfloateravatarinfo.h +++ b/indra/newview/llfloateravatarinfo.h @@ -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: diff --git a/indra/newview/llfloaterdisplayname.cpp b/indra/newview/llfloaterdisplayname.cpp new file mode 100644 index 000000000..05974b21d --- /dev/null +++ b/indra/newview/llfloaterdisplayname.cpp @@ -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("display_name_editor")->clear(); + getChild("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("lockout_text")->setTextArg("[TIME]", next_update_string); + getChild("lockout_text")->setVisible(true); + getChild("save_btn")->setEnabled(false); + getChild("display_name_editor")->setEnabled(false); + getChild("display_name_confirm")->setEnabled(false); + getChild("cancel_btn")->setFocus(TRUE); + + } + else + { + getChild("lockout_text")->setVisible(false); + getChild("save_btn")->setEnabled(true); + getChild("display_name_editor")->setEnabled(true); + getChild("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("display_name_editor")->getValue().asString(); + std::string display_name_confirm = self->getChild("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); +} + diff --git a/indra/newview/llfloaterdisplayname.h b/indra/newview/llfloaterdisplayname.h new file mode 100644 index 000000000..2d6bbf198 --- /dev/null +++ b/indra/newview/llfloaterdisplayname.h @@ -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 diff --git a/indra/newview/llnameeditor.cpp b/indra/newview/llnameeditor.cpp index 59973692d..2c2b27bef 100644 --- a/indra/newview/llnameeditor.cpp +++ b/indra/newview/llnameeditor.cpp @@ -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 { diff --git a/indra/newview/llnameeditor.h b/indra/newview/llnameeditor.h index 964682cee..3f9d6f5da 100644 --- a/indra/newview/llnameeditor.h +++ b/indra/newview/llnameeditor.h @@ -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 sInstances; + static void on_avatar_name_response(const LLUUID& agent_id, const LLAvatarName& av_name, void *userdata); private: LLUUID mNameID; diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index b59eaf844..8ee59ccab 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -83,15 +83,13 @@ #include "roles_constants.h" #include "lluictrlfactory.h" #include "llviewermenu.h" - +#include "llavatarnamecache.h" #include - - // [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) diff --git a/indra/newview/llpanelgeneral.cpp b/indra/newview/llpanelgeneral.cpp index ed07d1825..67d6f2ef1 100644 --- a/indra/newview/llpanelgeneral.cpp +++ b/indra/newview/llpanelgeneral.cpp @@ -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("fade_out_combobox"); fade_out_combobox->setCurrentByIndex(gSavedSettings.getS32("RenderName")); + LLComboBox* namesystem_combobox = getChild("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("fade_out_combobox"); gSavedSettings.setS32("RenderName", fade_out_combobox->getCurrentIndex()); + LLComboBox* namesystem_combobox = getChild("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")); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 8d0567946..272f0a3a0 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -205,6 +205,8 @@ #include "scriptcounter.h" // +#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 diff --git a/indra/newview/llviewerdisplayname.cpp b/indra/newview/llviewerdisplayname.cpp new file mode 100644 index 000000000..8aca75b43 --- /dev/null +++ b/indra/newview/llviewerdisplayname.cpp @@ -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 + gHTTPRegistrationMessageSetDisplayNameReply( + "/message/SetDisplayNameReply"); + +LLHTTPRegistration + gHTTPRegistrationMessageDisplayNameUpdate( + "/message/DisplayNameUpdate"); + diff --git a/indra/newview/llviewerdisplayname.h b/indra/newview/llviewerdisplayname.h new file mode 100644 index 000000000..6066cc880 --- /dev/null +++ b/indra/newview/llviewerdisplayname.h @@ -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 + +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 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 diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index e468d2edf..08c824424 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -250,6 +250,7 @@ // #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); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b53440b19..1ae475592 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -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; diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 09280a53f..67f0ff496 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -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 mObjectPartition; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 9e16a0f8b..7ddd85ee2 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -97,6 +97,9 @@ #include "llimagemetadatareader.h" // +#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 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() ) // + || 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(obj); + if (!avatar) return; + + avatar->clearNameTag(); +} + +//staticmNameString.empty() +void LLVOAvatar::invalidateNameTags() +{ + for (std::vector::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() { diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 2da35cb06..1b9f3381d 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -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 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; diff --git a/indra/newview/skins/default/xui/en-us/floater_display_name.xml b/indra/newview/skins/default/xui/en-us/floater_display_name.xml new file mode 100644 index 000000000..d31e9271e --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/floater_display_name.xml @@ -0,0 +1,29 @@ + + + + + Next possible change: [TIME]. + + + + New Display Name: + + + + Type your new name again to confirm: + + +