From 2cde962d5db94baf860eb94fecaf9671548b2c53 Mon Sep 17 00:00:00 2001 From: "dolphin@dolphin-THINK.lindenlab.com" Date: Mon, 19 Nov 2012 08:06:42 -0800 Subject: Test populating the experience keys UI with avatar name data. --- indra/llmessage/llexperiencecache.cpp | 138 +++++++++++++++++++++++++++++++--- indra/llmessage/llexperiencecache.h | 21 +++++- 2 files changed, 148 insertions(+), 11 deletions(-) (limited to 'indra/llmessage') diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 8667ae8981..0d8f76c7e2 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -25,24 +25,20 @@ */ #include "linden_common.h" +#include "llavatarname.h" #include "llframetimer.h" #include "llhttpclient.h" +#include "llsdserialize.h" #include #include #include "llexperiencecache.h" -class LLExperienceData -{ -public: - std::string mDisplayName; -}; - namespace LLExperienceCache { - bool sRunning = true; + bool sRunning = false; std::string sLookupURL; typedef std::set ask_queue_t; @@ -52,19 +48,122 @@ namespace LLExperienceCache pending_queue_t sPendingQueue; - typedef std::map cache_t; cache_t sCache; LLFrameTimer sRequestTimer; + + void processExperience( const LLUUID& agent_id, const LLExperienceData& experience, bool add_to_cache ) + { + if(add_to_cache) + { + sCache[agent_id]=experience; + + sPendingQueue.erase(agent_id); + + + //signal + } + } + + void initClass( bool running ) + { + sRunning = false; + } + + const cache_t& getCached() + { + return sCache; + } + + + + void importFile(std::istream& istr) + { + LLSD data; + S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr); + if(parse_count < 1) return; + + LLSD agents = data["agents"]; + + LLUUID agent_id; + LLExperienceData experience; + LLSD::map_const_iterator it = agents.beginMap(); + for(; it != agents.endMap() ; ++it) + { + agent_id.set(it->first); + experience.fromLLSD( it->second); + sCache[agent_id]=experience; + } + + LL_INFOS("ExperienceCache") << "loaded " << sCache.size() << LL_ENDL; + } + + void exportFile(std::ostream& ostr) + { + LLSD agents; + + cache_t::const_iterator it =sCache.begin(); + for( ; it != sCache.end() ; ++it) + { + agents[it->first.asString()] = it->second.asLLSD(); + } + + LLSD data; + data["agents"] = agents; + + LLSDSerialize::toPrettyXML(data, ostr); + } + class LLExperienceResponder : public LLHTTPClient::Responder { public: - LLExperienceResponder(std::vector agent_ids) + LLExperienceResponder(const std::vector& agent_ids) + :mAgentIds(agent_ids) { } + + virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content) + { + mHeaders = content; + } + + virtual void result(const LLSD& content) + { + 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(); + + LLExperienceData experience; + + if(experience.fromLLSD(row)) + { + LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "Received result for " << agent_id + << "display '" << experience.mDisplayName << "'" << LL_ENDL ; + + processExperience(agent_id, experience, true); + } + } + + LLSD unresolved_agents = content["bad_ids"]; + S32 num_unresolved = unresolved_agents.size(); + if(num_unresolved > 0) + { + LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "Ignoreing " << num_unresolved + << " bad ids" << LL_ENDL ; + } + + LL_DEBUGS("ExperienceCache") << __FUNCTION__ << sCache.size() << " cached experiences" << LL_ENDL; + } + + private: + std::vector mAgentIds; + LLSD mHeaders; }; void requestExperiences() @@ -171,7 +270,7 @@ namespace LLExperienceCache bool get( const LLUUID& agent_id, LLExperienceData* experience_data ) { - if(!sRunning) + if(sRunning) { cache_t::const_iterator it = sCache.find(agent_id); @@ -193,3 +292,22 @@ namespace LLExperienceCache } + +bool LLExperienceData::fromLLSD( const LLSD& sd ) +{ + mDisplayName = sd["display_name"].asString(); + mDescription = sd["username"].asString(); + + if(mDisplayName.empty() || mDescription.empty()) return false; + + mDescription += " % Hey, this is a description!"; + return true; +} + +LLSD LLExperienceData::asLLSD() const +{ + LLSD sd; + sd["display_name"] = mDisplayName; + sd["username"] = mDescription.substr(0, llmin(mDescription.size(),mDescription.find(" %"))); + return sd; +} diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index 6be51b039e..799cdea13a 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -32,7 +32,20 @@ #include class LLUUID; -class LLExperienceData; + + +class LLExperienceData +{ +public: + bool fromLLSD(const LLSD& sd); + LLSD asLLSD() const; + + + std::string mDisplayName; + std::string mDescription; +}; + + namespace LLExperienceCache { @@ -41,12 +54,18 @@ namespace LLExperienceCache void idle(); + void exportFile(std::ostream& ostr); + void importFile(std::istream& istr); + void initClass(bool running); void erase(const LLUUID& agent_id); void fetch(const LLUUID& agent_id); void insert(const LLUUID& agent_id, const LLExperienceData& experience_data); bool get(const LLUUID& agent_id, LLExperienceData* experience_data); + typedef std::map cache_t; + + const cache_t& getCached(); }; #endif // LL_LLEXPERIENCECACHE_H -- cgit v1.3 From 2566e0d08fd7e8a47fc690a8163c1273d91141c2 Mon Sep 17 00:00:00 2001 From: "dolphin@dolphin-THINK.lindenlab.com" Date: Mon, 26 Nov 2012 08:42:23 -0800 Subject: Added code for simulator integration. Added signals for cache update callbacks. Added expiration timers for cached experiences. --- indra/llmessage/llexperiencecache.cpp | 290 ++++++++++++++++++++++++++++++---- indra/llmessage/llexperiencecache.h | 18 ++- indra/newview/llstartup.cpp | 2 +- 3 files changed, 280 insertions(+), 30 deletions(-) (limited to 'indra/llmessage') diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 0d8f76c7e2..562c4c40df 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -31,6 +31,7 @@ #include "llsdserialize.h" #include #include +#include "boost\tokenizer.hpp" #include "llexperiencecache.h" @@ -38,7 +39,6 @@ namespace LLExperienceCache { - bool sRunning = false; std::string sLookupURL; typedef std::set ask_queue_t; @@ -47,29 +47,45 @@ namespace LLExperienceCache typedef std::map pending_queue_t; pending_queue_t sPendingQueue; - cache_t sCache; + int sMaximumLookups = 10; LLFrameTimer sRequestTimer; + // Periodically clean out expired entries from the cache + LLFrameTimer sEraseExpiredTimer; + + // 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; + + bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age); + void eraseExpired(); - void processExperience( const LLUUID& agent_id, const LLExperienceData& experience, bool add_to_cache ) + void processExperience( const LLUUID& agent_id, const LLExperienceData& experience ) { - if(add_to_cache) - { - sCache[agent_id]=experience; + sCache[agent_id]=experience; - sPendingQueue.erase(agent_id); + sPendingQueue.erase(agent_id); + + //signal + 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, experience); + sSignalMap.erase(agent_id); - //signal + delete signal; } } - void initClass( bool running ) + void initClass( ) { - sRunning = false; } const cache_t& getCached() @@ -77,6 +93,91 @@ namespace LLExperienceCache return sCache; } + void setMaximumLookups( int maximumLookups) + { + sMaximumLookups = maximumLookups; + } + + + bool 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)) + { + LL_DEBUGS("ExperienceCache") + << "got expiration from headers, max_age " << max_age + << LL_ENDL; + F64 now = LLFrameTimer::getTotalSeconds(); + *expires = now + (F64)max_age; + return true; + } + } + return false; + } + + + 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; + } void importFile(std::istream& istr) @@ -146,7 +247,7 @@ namespace LLExperienceCache LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "Received result for " << agent_id << "display '" << experience.mDisplayName << "'" << LL_ENDL ; - processExperience(agent_id, experience, true); + processExperience(agent_id, experience); } } @@ -154,13 +255,74 @@ namespace LLExperienceCache S32 num_unresolved = unresolved_agents.size(); if(num_unresolved > 0) { - LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "Ignoreing " << num_unresolved + LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "Ignoring " << num_unresolved << " bad ids" << LL_ENDL ; } LL_DEBUGS("ExperienceCache") << __FUNCTION__ << sCache.size() << " cached experiences" << LL_ENDL; } + 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); + + LLExperienceData experience; + experience.mDisplayName = LLExperienceCache::DUMMY_NAME; + experience.mDescription = LLExperienceCache::DUMMY_NAME; + experience.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) + { + LLExperienceCache::processExperience((*it), experience); + } + } + + // 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 (LLExperienceCache::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; + } + } + private: std::vector mAgentIds; LLSD mHeaders; @@ -186,13 +348,15 @@ namespace LLExperienceCache std::string arg="?ids="; - for(ask_queue_t::const_iterator it = sAskQueue.begin(); it != sAskQueue.end() ; ++it) + int request_count = 0; + for(ask_queue_t::const_iterator it = sAskQueue.begin() ; it != sAskQueue.end() && request_count < sMaximumLookups; ++it) { const LLUUID& agent_id = *it; url += arg; url += agent_id.asString(); agent_ids.push_back(agent_id); + request_count++; sPendingQueue[agent_id] = now; @@ -244,7 +408,20 @@ namespace LLExperienceCache void idle() { - sRunning = true; + + const F32 SECS_BETWEEN_REQUESTS = 0.1f; + if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) + { + return; + } + + // Must be large relative to above + const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds + if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) + { + eraseExpired(); + } + if(!sAskQueue.empty()) { @@ -257,6 +434,25 @@ namespace LLExperienceCache sCache.erase(agent_id); } + void eraseExpired() + { + S32 expired_count = 0; + F64 now = LLFrameTimer::getTotalSeconds(); + cache_t::iterator it = sCache.begin(); + while (it != sCache.end()) + { + cache_t::iterator cur = it; + ++it; + const LLExperienceData& experience = cur->second; + if (experience.mExpires < now) + { + sCache.erase(cur); + expired_count++; + } + } + } + + void fetch( const LLUUID& agent_id ) { LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "queue request for agent" << agent_id << LL_ENDL ; @@ -270,16 +466,12 @@ namespace LLExperienceCache bool get( const LLUUID& agent_id, LLExperienceData* experience_data ) { - if(sRunning) + cache_t::const_iterator it = sCache.find(agent_id); + if (it != sCache.end()) { - - cache_t::const_iterator it = sCache.find(agent_id); - if (it != sCache.end()) - { - llassert(experience_data); - *experience_data = it->second; - return true; - } + llassert(experience_data); + *experience_data = it->second; + return true; } if(!isRequestPending(agent_id)) @@ -291,12 +483,54 @@ namespace LLExperienceCache } + + void get( const LLUUID& agent_id, callback_slot_t slot ) + { + cache_t::const_iterator it = sCache.find(agent_id); + if (it != sCache.end()) + { + // ...name already exists in cache, fire callback now + callback_signal_t signal; + signal.connect(slot); + signal(agent_id, it->second); + return; + } + + // schedule a request + if (!isRequestPending(agent_id)) + { + sAskQueue.insert(agent_id); + } + + // always store additional callback, even if request is pending + signal_map_t::iterator sig_it = sSignalMap.find(agent_id); + if (sig_it == sSignalMap.end()) + { + // ...new callback for this id + callback_signal_t* signal = new callback_signal_t(); + signal->connect(slot); + sSignalMap[agent_id] = signal; + } + else + { + // ...existing callback, bind additional slot + callback_signal_t* signal = sig_it->second; + signal->connect(slot); + } + } + } +static const std::string EXPERIENCE_NAME("username"); +static const std::string EXPERIENCE_DESCRIPTION("display_name"); +static const std::string EXPERIENCE_EXPIRATION("display_name_expires"); + bool LLExperienceData::fromLLSD( const LLSD& sd ) { - mDisplayName = sd["display_name"].asString(); - mDescription = sd["username"].asString(); + mDisplayName = sd[EXPERIENCE_NAME].asString(); + mDescription = sd[EXPERIENCE_DESCRIPTION].asString(); + LLDate expiration = sd[EXPERIENCE_EXPIRATION]; + mExpires = expiration.secondsSinceEpoch(); if(mDisplayName.empty() || mDescription.empty()) return false; @@ -307,7 +541,9 @@ bool LLExperienceData::fromLLSD( const LLSD& sd ) LLSD LLExperienceData::asLLSD() const { LLSD sd; - sd["display_name"] = mDisplayName; - sd["username"] = mDescription.substr(0, llmin(mDescription.size(),mDescription.find(" %"))); + sd[EXPERIENCE_NAME] = mDisplayName; + sd[EXPERIENCE_DESCRIPTION] = mDescription.substr(0, llmin(mDescription.size(),mDescription.find(" %"))); + sd[EXPERIENCE_EXPIRATION] = LLDate(mExpires); + return sd; } diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index 799cdea13a..2f2647d5a3 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -30,6 +30,7 @@ #define LL_LLEXPERIENCECACHE_H #include +#include class LLUUID; @@ -43,27 +44,40 @@ public: std::string mDisplayName; std::string mDescription; + F64 mExpires; }; namespace LLExperienceCache { + // dummy name used when we have nothing else + const std::string DUMMY_NAME = "\?\?\?"; + // Callback types for get() below + typedef boost::signals2::signal< + void (const LLUUID& agent_id, const LLExperienceData& experience)> + callback_signal_t; + typedef callback_signal_t::slot_type callback_slot_t; + typedef std::map cache_t; + + void setLookupURL(const std::string& lookup_url); bool hasLookupURL(); + void setMaximumLookups(int maximumLookups); void idle(); void exportFile(std::ostream& ostr); void importFile(std::istream& istr); - void initClass(bool running); + void initClass(); void erase(const LLUUID& agent_id); void fetch(const LLUUID& agent_id); void insert(const LLUUID& agent_id, const LLExperienceData& experience_data); bool get(const LLUUID& agent_id, LLExperienceData* experience_data); - typedef std::map cache_t; + // If name information is in cache, callback will be called immediately. + void get(const LLUUID& agent_id, callback_slot_t slot); const cache_t& getCached(); }; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index ec8415b28e..a58a26b6fe 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2817,7 +2817,7 @@ void LLStartUp::initNameCache() void LLStartUp::initExperienceCache() { LLAppViewer::instance()->loadExperienceCache(); - LLExperienceCache::initClass(false); + LLExperienceCache::initClass(); } void LLStartUp::cleanupNameCache() -- cgit v1.3 From 179e944f45476e03eb7b828e427f9d299529ad12 Mon Sep 17 00:00:00 2001 From: dolphin Date: Tue, 18 Dec 2012 16:06:00 -0800 Subject: Updated client to use new caps brought over server changes to llExperienceCache --- indra/llmessage/llexperiencecache.cpp | 353 ++++++++++++++++++++-------------- indra/llmessage/llexperiencecache.h | 45 +++-- indra/newview/llappviewer.cpp | 4 +- indra/newview/llpanelexperiences.cpp | 90 +++++++-- indra/newview/llpanelexperiences.h | 5 +- indra/newview/llviewerregion.cpp | 2 + indra/newview/llvoavatar.cpp | 5 +- 7 files changed, 306 insertions(+), 198 deletions(-) (limited to 'indra/llmessage') diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 562c4c40df..18b950b61e 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -23,25 +23,26 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ +#include "llexperiencecache.h" -#include "linden_common.h" #include "llavatarname.h" #include "llframetimer.h" #include "llhttpclient.h" #include "llsdserialize.h" #include #include -#include "boost\tokenizer.hpp" +#include "boost/tokenizer.hpp" + -#include "llexperiencecache.h" namespace LLExperienceCache { + const std::string& MAP_KEY = PUBLIC_KEY; std::string sLookupURL; - typedef std::set ask_queue_t; + typedef std::map ask_queue_t; ask_queue_t sAskQueue; typedef std::map pending_queue_t; @@ -65,20 +66,40 @@ namespace LLExperienceCache bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age); void eraseExpired(); - void processExperience( const LLUUID& agent_id, const LLExperienceData& experience ) + void processExperience( const LLUUID& public_key, const LLSD& experience ) { - sCache[agent_id]=experience; + sCache[public_key]=experience; + LLSD & row = sCache[public_key]; + + if(row.has("expires")) + { + row["expires"] = row["expires"].asReal() + LLFrameTimer::getTotalSeconds(); + } + + if(row.has(PUBLIC_KEY)) + { + sPendingQueue.erase(row[PUBLIC_KEY].asUUID()); + } + + if(row.has(PRIVATE_KEY)) + { + sPendingQueue.erase(row[PRIVATE_KEY].asUUID()); + } + + if(row.has(CREATOR_KEY)) + { + sPendingQueue.erase(row[CREATOR_KEY].asUUID()); + } - sPendingQueue.erase(agent_id); //signal - signal_map_t::iterator sig_it = sSignalMap.find(agent_id); + signal_map_t::iterator sig_it = sSignalMap.find(public_key); if (sig_it != sSignalMap.end()) { callback_signal_t* signal = sig_it->second; - (*signal)(agent_id, experience); + (*signal)(experience); - sSignalMap.erase(agent_id); + sSignalMap.erase(public_key); delete signal; } @@ -109,7 +130,7 @@ namespace LLExperienceCache std::string cache_control = cache_control_header.asString(); if (max_age_from_cache_control(cache_control, &max_age)) { - LL_DEBUGS("ExperienceCache") + LL_WARNS("ExperienceCache") << "got expiration from headers, max_age " << max_age << LL_ENDL; F64 now = LLFrameTimer::getTotalSeconds(); @@ -186,16 +207,14 @@ namespace LLExperienceCache S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr); if(parse_count < 1) return; - LLSD agents = data["agents"]; + LLSD experiences = data["experiences"]; - LLUUID agent_id; - LLExperienceData experience; - LLSD::map_const_iterator it = agents.beginMap(); - for(; it != agents.endMap() ; ++it) + LLUUID public_key; + LLSD::map_const_iterator it = experiences.beginMap(); + for(; it != experiences.endMap() ; ++it) { - agent_id.set(it->first); - experience.fromLLSD( it->second); - sCache[agent_id]=experience; + public_key.set(it->first); + sCache[public_key]=it->second; } LL_INFOS("ExperienceCache") << "loaded " << sCache.size() << LL_ENDL; @@ -203,16 +222,20 @@ namespace LLExperienceCache void exportFile(std::ostream& ostr) { - LLSD agents; + LLSD experiences; cache_t::const_iterator it =sCache.begin(); for( ; it != sCache.end() ; ++it) { - agents[it->first.asString()] = it->second.asLLSD(); + if(!it->second.has(PUBLIC_KEY) || it->second[PUBLIC_KEY].asUUID().isNull() || + it->second.has("error")) + continue; + + experiences[it->first.asString()] = it->second; } LLSD data; - data["agents"] = agents; + data["experiences"] = experiences; LLSDSerialize::toPrettyXML(data, ostr); } @@ -220,8 +243,8 @@ namespace LLExperienceCache class LLExperienceResponder : public LLHTTPClient::Responder { public: - LLExperienceResponder(const std::vector& agent_ids) - :mAgentIds(agent_ids) + LLExperienceResponder(const ask_queue_t& keys) + :mKeys(keys) { } @@ -233,60 +256,65 @@ namespace LLExperienceCache virtual void result(const LLSD& content) { - LLSD agents = content["agents"]; - LLSD::array_const_iterator it = agents.beginArray(); - for( /**/ ; it != agents.endArray(); ++it) + LLSD experiences = content["experience_keys"]; + LLSD::array_const_iterator it = experiences.beginArray(); + for( /**/ ; it != experiences.endArray(); ++it) { const LLSD& row = *it; - LLUUID agent_id = row["id"].asUUID(); + LLUUID public_key = row[PUBLIC_KEY].asUUID(); - LLExperienceData experience; - if(experience.fromLLSD(row)) - { - LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "Received result for " << agent_id - << "display '" << experience.mDisplayName << "'" << LL_ENDL ; + LL_INFOS("ExperienceCache") << "Received result for " << public_key + << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL ; - processExperience(agent_id, experience); - } + processExperience(public_key, row); } - LLSD unresolved_agents = content["bad_ids"]; - S32 num_unresolved = unresolved_agents.size(); - if(num_unresolved > 0) + LLSD error_ids = content["error_ids"]; + LLSD::map_const_iterator errIt = error_ids.beginMap(); + for( /**/ ; errIt != error_ids.endMap() ; ++errIt ) { - LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "Ignoring " << num_unresolved - << " bad ids" << LL_ENDL ; + LLUUID id = LLUUID(errIt->first); + for( it = errIt->second.beginArray(); it != errIt->second.endArray() ; ++it) + { + LL_INFOS("ExperienceCache") << "Clearing error result for " << id + << " of type '" << it->asString() << "'" << LL_ENDL ; + + erase(id, it->asString()); + } } - LL_DEBUGS("ExperienceCache") << __FUNCTION__ << sCache.size() << " cached experiences" << LL_ENDL; + LL_INFOS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL; } - void error(U32 status, const std::string& reason) + 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); - - LLExperienceData experience; - experience.mDisplayName = LLExperienceCache::DUMMY_NAME; - experience.mDescription = LLExperienceCache::DUMMY_NAME; - experience.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) + LL_WARNS("ExperienceCache") << "Request failed "<second] = it->first; + exp["key_type"] = it->second; + exp["uuid"] = it->first; + exp["error"] = (LLSD::Integer)status; + LLExperienceCache::processExperience(it->first, exp); + } + } // 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"]; @@ -297,7 +325,7 @@ namespace LLExperienceCache if (delta_seconds > 0) { // ...valid delta-seconds - return now + F64(delta_seconds); + return F64(delta_seconds); } } @@ -313,78 +341,87 @@ namespace LLExperienceCache { // ...service unavailable, retry soon const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min - return now + SERVICE_UNAVAILABLE_DELAY; + return SERVICE_UNAVAILABLE_DELAY; + } + else if (status == 499) + { + // ...we were probably too busy, retry quickly + const F64 BUSY_DELAY = 10.0; // 10 seconds + return BUSY_DELAY; + } else { // ...other unexpected error const F64 DEFAULT_DELAY = 3600.0; // 1 hour - return now + DEFAULT_DELAY; + return DEFAULT_DELAY; } } private: - std::vector mAgentIds; + ask_queue_t mKeys; LLSD mHeaders; }; void requestExperiences() { - if(sAskQueue.empty()) + if(sAskQueue.empty() || sLookupURL.empty()) return; F64 now = LLFrameTimer::getTotalSeconds(); - const U32 NAME_URL_MAX = 4096; - const U32 NAME_URL_SEND_THRESHOLD = 3000; + const U32 EXP_URL_SEND_THRESHOLD = 3000; - std::string url; - url.reserve(NAME_URL_MAX); - std::vector agent_ids; - agent_ids.reserve(128); + std::ostringstream ostr; - url += sLookupURL; + ask_queue_t keys; - std::string arg="?ids="; + ostr << sLookupURL; + + char arg='?'; int request_count = 0; for(ask_queue_t::const_iterator it = sAskQueue.begin() ; it != sAskQueue.end() && request_count < sMaximumLookups; ++it) { - const LLUUID& agent_id = *it; + const LLUUID& key = it->first; + const std::string& key_type = it->second; + + ostr << arg << key_type << '=' << key.asString() ; - url += arg; - url += agent_id.asString(); - agent_ids.push_back(agent_id); + keys[key]=key_type; request_count++; - sPendingQueue[agent_id] = now; + sPendingQueue[key] = now; - arg[0]='&'; + arg='&'; - if(url.size() > NAME_URL_SEND_THRESHOLD) + if(ostr.tellp() > EXP_URL_SEND_THRESHOLD) { - LLHTTPClient::get(url, new LLExperienceResponder(agent_ids)); - url = sLookupURL; - arg[0]='?'; - agent_ids.clear(); + LL_INFOS("ExperienceCache") << " query: " << ostr.str() << LL_ENDL; + LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); + ostr.clear(); + ostr.str(sLookupURL); + arg='?'; + keys.clear(); } } - if(url.size() > sLookupURL.size()) + if(ostr.tellp() > sLookupURL.size()) { - LLHTTPClient::get(url, new LLExperienceResponder(agent_ids)); + LL_INFOS("ExperienceCache") << " query: " << ostr.str() << LL_ENDL; + LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); } sAskQueue.clear(); } - bool isRequestPending(const LLUUID& agent_id) + bool isRequestPending(const LLUUID& public_key) { bool isPending = false; const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; - pending_queue_t::const_iterator it = sPendingQueue.find(agent_id); + pending_queue_t::const_iterator it = sPendingQueue.find(public_key); if(it != sPendingQueue.end()) { @@ -399,6 +436,10 @@ namespace LLExperienceCache void setLookupURL( const std::string& lookup_url ) { sLookupURL = lookup_url; + if(!sLookupURL.empty()) + { + sLookupURL += "id/"; + } } bool hasLookupURL() @@ -429,87 +470,136 @@ namespace LLExperienceCache } } - void erase( const LLUUID& agent_id ) + struct FindByKey + { + FindByKey(const LLUUID& key, const std::string& key_type):mKey(key), mKeyType(key_type){} + const LLUUID& mKey; + const std::string& mKeyType; + + bool operator()(cache_t::value_type& experience) + { + return experience.second.has(mKeyType) && experience.second[mKeyType].asUUID() == mKey; + } + }; + + + cache_t::iterator Find(const LLUUID& key, const std::string& key_type) + { + LL_INFOS("ExperienceCache") << " searching for " << key << " of type " << key_type << LL_ENDL; + if(key_type == MAP_KEY) + { + return sCache.find(key); + } + + return std::find_if(sCache.begin(), sCache.end(), FindByKey(key, key_type)); + } + + + void erase( const LLUUID& key, const std::string& key_type ) { - sCache.erase(agent_id); + cache_t::iterator it = Find(key, key_type); + + if(it != sCache.end()) + { + sCache.erase(it); + } } void eraseExpired() { - S32 expired_count = 0; F64 now = LLFrameTimer::getTotalSeconds(); cache_t::iterator it = sCache.begin(); while (it != sCache.end()) { cache_t::iterator cur = it; + LLSD& exp = cur->second; ++it; - const LLExperienceData& experience = cur->second; - if (experience.mExpires < now) + if(exp.has("expires") && exp["expires"].asReal() < now) { - sCache.erase(cur); - expired_count++; + if(exp.has("key_type") && exp.has("uuid")) + { + fetch(exp["uuid"].asUUID(), exp["key_type"].asString(), true); + sCache.erase(cur); + } + else if(exp.has(MAP_KEY)) + { + LLUUID id = exp[MAP_KEY]; + if(!id.isNull()) + { + fetch(id, MAP_KEY, true); + } + } } } } - - void fetch( const LLUUID& agent_id ) + + bool fetch( const LLUUID& key, const std::string& key_type, bool refresh/* = true*/ ) { - LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "queue request for agent" << agent_id << LL_ENDL ; - sAskQueue.insert(agent_id); + if(!key.isNull() && !isRequestPending(key) && (refresh || Find(key, key_type)==sCache.end())) + { + LL_INFOS("ExperienceCache") << " queue request for " << key_type << " " << key << LL_ENDL ; + sAskQueue[key]=key_type; + + return true; + } + return false; } - void insert( const LLUUID& agent_id, const LLExperienceData& experience_data ) + void insert(const LLSD& experience_data ) { - sCache[agent_id]=experience_data; + if(experience_data.has(MAP_KEY)) + { + sCache[experience_data[MAP_KEY].asUUID()]=experience_data; + } + else + { + LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << MAP_KEY << LL_ENDL; + } } - bool get( const LLUUID& agent_id, LLExperienceData* experience_data ) + bool get( const LLUUID& key, const std::string& key_type, LLSD& experience_data ) { - cache_t::const_iterator it = sCache.find(agent_id); + if(key.isNull()) return false; + cache_t::const_iterator it = Find(key, key_type); + if (it != sCache.end()) { - llassert(experience_data); - *experience_data = it->second; + experience_data = it->second; return true; } - if(!isRequestPending(agent_id)) - { - fetch(agent_id); - } + fetch(key, key_type); return false; } - - void get( const LLUUID& agent_id, callback_slot_t slot ) + void get( const LLUUID& key, const std::string& key_type, callback_slot_t slot ) { - cache_t::const_iterator it = sCache.find(agent_id); + if(key.isNull()) return; + + cache_t::const_iterator it = Find(key, key_type); if (it != sCache.end()) { // ...name already exists in cache, fire callback now callback_signal_t signal; signal.connect(slot); - signal(agent_id, it->second); + + signal(it->second); return; } - // schedule a request - if (!isRequestPending(agent_id)) - { - sAskQueue.insert(agent_id); - } + fetch(key, key_type); // always store additional callback, even if request is pending - signal_map_t::iterator sig_it = sSignalMap.find(agent_id); + signal_map_t::iterator sig_it = sSignalMap.find(key); 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; + sSignalMap[key] = signal; } else { @@ -520,30 +610,3 @@ namespace LLExperienceCache } } - -static const std::string EXPERIENCE_NAME("username"); -static const std::string EXPERIENCE_DESCRIPTION("display_name"); -static const std::string EXPERIENCE_EXPIRATION("display_name_expires"); - -bool LLExperienceData::fromLLSD( const LLSD& sd ) -{ - mDisplayName = sd[EXPERIENCE_NAME].asString(); - mDescription = sd[EXPERIENCE_DESCRIPTION].asString(); - LLDate expiration = sd[EXPERIENCE_EXPIRATION]; - mExpires = expiration.secondsSinceEpoch(); - - if(mDisplayName.empty() || mDescription.empty()) return false; - - mDescription += " % Hey, this is a description!"; - return true; -} - -LLSD LLExperienceData::asLLSD() const -{ - LLSD sd; - sd[EXPERIENCE_NAME] = mDisplayName; - sd[EXPERIENCE_DESCRIPTION] = mDescription.substr(0, llmin(mDescription.size(),mDescription.find(" %"))); - sd[EXPERIENCE_EXPIRATION] = LLDate(mExpires); - - return sd; -} diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index 2f2647d5a3..396a244935 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -29,36 +29,35 @@ #ifndef LL_LLEXPERIENCECACHE_H #define LL_LLEXPERIENCECACHE_H -#include +#include "linden_common.h" #include +class LLSD; class LLUUID; -class LLExperienceData -{ -public: - bool fromLLSD(const LLSD& sd); - LLSD asLLSD() const; - - - std::string mDisplayName; - std::string mDescription; - F64 mExpires; -}; - - namespace LLExperienceCache { + const std::string PUBLIC_KEY = "public-id"; + const std::string PRIVATE_KEY = "private-id"; + const std::string CREATOR_KEY = "creator-id"; + const std::string NAME = "name"; + const std::string PROPERTIES = "properties"; + const std::string EXPIRES = "expires"; + + const int EXPERIENCE_INVALID = (1 << 0); + const int EXPERIENCE_NORMAL = (1 << 1); + const int EXPERIENCE_REGION = (1 << 2); + const static F64 DEFAULT_EXPIRATION = 600.0; + // dummy name used when we have nothing else const std::string DUMMY_NAME = "\?\?\?"; // Callback types for get() below - typedef boost::signals2::signal< - void (const LLUUID& agent_id, const LLExperienceData& experience)> + typedef boost::signals2::signal callback_signal_t; typedef callback_signal_t::slot_type callback_slot_t; - typedef std::map cache_t; + typedef std::map cache_t; void setLookupURL(const std::string& lookup_url); @@ -70,14 +69,14 @@ namespace LLExperienceCache void exportFile(std::ostream& ostr); void importFile(std::istream& istr); void initClass(); - - void erase(const LLUUID& agent_id); - void fetch(const LLUUID& agent_id); - void insert(const LLUUID& agent_id, const LLExperienceData& experience_data); - bool get(const LLUUID& agent_id, LLExperienceData* experience_data); + + void erase(const LLUUID& key, const std::string& key_type); + bool fetch(const LLUUID& key, const std::string& key_type, bool refresh = false); + void insert(LLSD& experience_data); + bool get(const LLUUID& key, const std::string& key_type, LLSD& experience_data); // If name information is in cache, callback will be called immediately. - void get(const LLUUID& agent_id, callback_slot_t slot); + void get(const LLUUID& key, const std::string& key_type, callback_slot_t slot); const cache_t& getCached(); }; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e49212d9de..c4fdc1ee21 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4818,12 +4818,12 @@ void LLAppViewer::idleExperienceCache() LLViewerRegion* region = gAgent.getRegion(); if (!region) return; - std::string lookup_url=region->getCapability("GetDisplayNames"); // use GetDisplayNames for testing round trip + std::string lookup_url=region->getCapability("GetExperienceInfo"); if(!lookup_url.empty() && lookup_url.back() != '/') { lookup_url += '/'; } - + LLExperienceCache::setLookupURL(lookup_url); LLExperienceCache::idle(); diff --git a/indra/newview/llpanelexperiences.cpp b/indra/newview/llpanelexperiences.cpp index bc9d3cc0ee..02a3c4c410 100644 --- a/indra/newview/llpanelexperiences.cpp +++ b/indra/newview/llpanelexperiences.cpp @@ -4,6 +4,7 @@ #include "llpanelprofile.h" #include "lluictrlfactory.h" #include "llexperiencecache.h" +#include "llagent.h" #include "llpanelexperiences.h" @@ -26,6 +27,61 @@ void* LLPanelExperiences::create( void* data ) return new LLPanelExperiences(); } +void ExperienceResult(LLHandle panel, const LLSD& experience) +{ + LLPanelExperiences* experiencePanel = panel.get(); + if(experiencePanel) + { + experiencePanel->addExperienceInfo(experience); + } +} + +class LLExperienceListResponder : public LLHTTPClient::Responder +{ +public: + LLExperienceListResponder(const LLHandle& parent):mParent(parent) + { + } + + LLHandle mParent; + + virtual void result(const LLSD& content) + { + if(mParent.isDead()) + return; + + LLSD experiences = content["experiences"]; + LLSD::array_const_iterator it = experiences.beginArray(); + for( /**/ ; it != experiences.endArray(); ++it) + { + LLUUID public_key = it->asUUID(); + + LLExperienceCache::get(public_key, LLExperienceCache::PUBLIC_KEY, boost::bind(ExperienceResult, mParent, _1)); + } + } +}; + +void LLPanelExperiences::addExperienceInfo(const LLSD& experience) +{ + LLExperienceItem* item = new LLExperienceItem(); + if(experience.has(LLExperienceCache::NAME)) + { + item->setExperienceName(experience[LLExperienceCache::NAME].asString()); + } + else if(experience.has("error")) + { + item->setExperienceName(experience["error"].asString()); + } + + if(experience.has(LLExperienceCache::PUBLIC_KEY)) + { + item->setExperienceDescription(experience[LLExperienceCache::PUBLIC_KEY].asString()); + } + + mExperiencesList->addItem(item); + +} + BOOL LLPanelExperiences::postBuild( void ) { @@ -35,15 +91,15 @@ BOOL LLPanelExperiences::postBuild( void ) mExperiencesList->setNoItemsCommentText(getString("no_experiences")); } - const LLExperienceCache::cache_t& experiences = LLExperienceCache::getCached(); - LLExperienceCache::cache_t::const_iterator it = experiences.begin(); - for( ; it != experiences.end() && mExperiencesList->getChildCount() < 10 ; ++it) + LLViewerRegion* region = gAgent.getRegion(); + if (region) { - LLExperienceItem* item = new LLExperienceItem(); - item->setExperienceName(it->second.mDisplayName); - item->setExperienceDescription(it->second.mDescription); - mExperiencesList->addItem(item); + std::string lookup_url=region->getCapability("GetExperiences"); + if(!lookup_url.empty()) + { + LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle())); + } } mExperiencesAccTab = getChild("tab_experiences"); @@ -71,17 +127,6 @@ void LLPanelExperiences::updateData() if(isDirty()) { mNoExperiences = false; - - /* - mNoItemsLabel->setValue(LLTrans::getString("PicksClassifiedsLoadingText")); - mNoItemsLabel->setVisible(TRUE); - - mPicksList->clear(); - LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(getAvatarId()); - - mClassifiedsList->clear(); - LLAvatarPropertiesProcessor::getInstance()->sendAvatarClassifiedsRequest(getAvatarId()); - */ } } @@ -179,10 +224,13 @@ LLExperienceItem::LLExperienceItem() buildFromFile("panel_experience_info.xml"); } -void LLExperienceItem::init( LLExperienceData* experience_data ) +void LLExperienceItem::init( LLSD* experience_data ) { - setExperienceDescription(experience_data->mDescription); - setExperienceName(experience_data->mDisplayName); + if(experience_data) + { + setExperienceDescription(experience_data->has(LLExperienceCache::PUBLIC_KEY)?(*experience_data)[LLExperienceCache::PUBLIC_KEY].asString() : std::string()); + setExperienceName(experience_data->has(LLExperienceCache::NAME)?(*experience_data)[LLExperienceCache::NAME].asString() : std::string()); + } } void LLExperienceItem::setExperienceDescription( const std::string& val ) diff --git a/indra/newview/llpanelexperiences.h b/indra/newview/llpanelexperiences.h index 33bb0f944a..1fe3f6ae1d 100644 --- a/indra/newview/llpanelexperiences.h +++ b/indra/newview/llpanelexperiences.h @@ -31,7 +31,6 @@ #include "llflatlistview.h" #include "llpanelavatar.h" -class LLExperienceData; class LLExperienceItem; class LLPanelProfile; @@ -69,7 +68,7 @@ public: LLExperienceItem* getSelectedExperienceItem(); void setProfilePanel(LLPanelProfile* profile_panel); - + void addExperienceInfo(const LLSD& experience); protected: void onListCommit(const LLFlatListView* f_list); @@ -97,7 +96,7 @@ public: LLExperienceItem(); ~LLExperienceItem(); - void init(LLExperienceData* experience_data); + void init(LLSD* experience_data); /*virtual*/ BOOL postBuild(); void update(); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b607afbd9d..7417e4fa06 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1536,6 +1536,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) } capabilityNames.append("GetDisplayNames"); + capabilityNames.append("GetExperiences"); + capabilityNames.append("GetExperienceInfo"); capabilityNames.append("GetMesh"); capabilityNames.append("GetObjectCost"); capabilityNames.append("GetObjectPhysicsData"); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 4635138fa3..2d89db6e28 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2517,10 +2517,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) idleUpdateBelowWater(); // wind effect uses this idleUpdateWindEffect(); } - - LLExperienceData ed; - LLExperienceCache::get(getID(), &ed); - + idleUpdateNameTag( root_pos_last ); idleUpdateRenderCost(); } -- cgit v1.3 From 61874872a7b4b8909f835650ab6732e24c61ffa2 Mon Sep 17 00:00:00 2001 From: dolphin Date: Wed, 13 Feb 2013 14:20:35 -0800 Subject: Updated experience cache to latest web service api Added new CAP request for experiences the avatar can sign --- indra/llmessage/llexperiencecache.cpp | 156 ++++++++++++++++++++-------------- indra/llmessage/llexperiencecache.h | 36 +++++--- indra/newview/llpanelexperiences.cpp | 8 +- indra/newview/llpreviewscript.cpp | 10 +-- indra/newview/llviewerregion.cpp | 1 + 5 files changed, 128 insertions(+), 83 deletions(-) (limited to 'indra/llmessage') diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 18b950b61e..1a6e74d123 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -34,12 +34,15 @@ #include "boost/tokenizer.hpp" +namespace LLExperienceCache +{ + typedef std::map PrivateKeyMap; + PrivateKeyMap experinceKeyMap; + + void mapPrivateKeys(const LLSD& legacyKeys); -namespace LLExperienceCache -{ - const std::string& MAP_KEY = PUBLIC_KEY; std::string sLookupURL; typedef std::map ask_queue_t; @@ -76,19 +79,14 @@ namespace LLExperienceCache row["expires"] = row["expires"].asReal() + LLFrameTimer::getTotalSeconds(); } - if(row.has(PUBLIC_KEY)) - { - sPendingQueue.erase(row[PUBLIC_KEY].asUUID()); - } - - if(row.has(PRIVATE_KEY)) + if(row.has(EXPERIENCE_ID)) { - sPendingQueue.erase(row[PRIVATE_KEY].asUUID()); + sPendingQueue.erase(row[EXPERIENCE_ID].asUUID()); } - if(row.has(CREATOR_KEY)) + if(row.has(OWNER_ID)) { - sPendingQueue.erase(row[CREATOR_KEY].asUUID()); + sPendingQueue.erase(row[OWNER_ID].asUUID()); } @@ -119,6 +117,31 @@ namespace LLExperienceCache sMaximumLookups = maximumLookups; } + void bootstrap(const LLSD& legacyKeys, int initialExpiration) + { + mapPrivateKeys(legacyKeys); + LLSD::array_const_iterator it = legacyKeys.beginArray(); + for(/**/; it != legacyKeys.endArray(); ++it) + { + LLSD experience = *it; + if(experience.has(EXPERIENCE_ID)) + { + if(!experience.has("expires")) + { + experience["expires"] = initialExpiration; + } + processExperience(experience[EXPERIENCE_ID].asUUID(), experience); + } + else + { + LL_WARNS("ExperienceCache") + << "Skipping bootstrap entry which is missing " << EXPERIENCE_ID + << LL_ENDL; + } + } + } + + bool expirationFromCacheControl(LLSD headers, F64 *expires) { @@ -227,7 +250,7 @@ namespace LLExperienceCache cache_t::const_iterator it =sCache.begin(); for( ; it != sCache.end() ; ++it) { - if(!it->second.has(PUBLIC_KEY) || it->second[PUBLIC_KEY].asUUID().isNull() || + if(!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() || it->second.has("error")) continue; @@ -261,7 +284,7 @@ namespace LLExperienceCache for( /**/ ; it != experiences.endArray(); ++it) { const LLSD& row = *it; - LLUUID public_key = row[PUBLIC_KEY].asUUID(); + LLUUID public_key = row[EXPERIENCE_ID].asUUID(); LL_INFOS("ExperienceCache") << "Received result for " << public_key @@ -280,7 +303,7 @@ namespace LLExperienceCache LL_INFOS("ExperienceCache") << "Clearing error result for " << id << " of type '" << it->asString() << "'" << LL_ENDL ; - erase(id, it->asString()); + erase(id); } } @@ -301,8 +324,8 @@ namespace LLExperienceCache for ( ; it != mKeys.end(); ++it) { LLSD exp; - exp["expires"]=retry_timestamp; - exp[it->second] = it->first; + exp[EXPIRES]=retry_timestamp; + exp[EXPERIENCE_ID] = it->first; exp["key_type"] = it->second; exp["uuid"] = it->first; exp["error"] = (LLSD::Integer)status; @@ -470,34 +493,9 @@ namespace LLExperienceCache } } - struct FindByKey + void erase( const LLUUID& key ) { - FindByKey(const LLUUID& key, const std::string& key_type):mKey(key), mKeyType(key_type){} - const LLUUID& mKey; - const std::string& mKeyType; - - bool operator()(cache_t::value_type& experience) - { - return experience.second.has(mKeyType) && experience.second[mKeyType].asUUID() == mKey; - } - }; - - - cache_t::iterator Find(const LLUUID& key, const std::string& key_type) - { - LL_INFOS("ExperienceCache") << " searching for " << key << " of type " << key_type << LL_ENDL; - if(key_type == MAP_KEY) - { - return sCache.find(key); - } - - return std::find_if(sCache.begin(), sCache.end(), FindByKey(key, key_type)); - } - - - void erase( const LLUUID& key, const std::string& key_type ) - { - cache_t::iterator it = Find(key, key_type); + cache_t::iterator it = sCache.find(key); if(it != sCache.end()) { @@ -518,15 +516,15 @@ namespace LLExperienceCache { if(exp.has("key_type") && exp.has("uuid")) { - fetch(exp["uuid"].asUUID(), exp["key_type"].asString(), true); + fetch(exp[EXPERIENCE_ID].asUUID(), true); sCache.erase(cur); } - else if(exp.has(MAP_KEY)) + else if(exp.has(EXPERIENCE_ID)) { - LLUUID id = exp[MAP_KEY]; - if(!id.isNull()) + LLUUID id = exp[EXPERIENCE_ID].asUUID(); + if(id.notNull()) { - fetch(id, MAP_KEY, true); + fetch(id, true); } } } @@ -534,12 +532,12 @@ namespace LLExperienceCache } - bool fetch( const LLUUID& key, const std::string& key_type, bool refresh/* = true*/ ) + bool fetch( const LLUUID& key, bool refresh/* = true*/ ) { - if(!key.isNull() && !isRequestPending(key) && (refresh || Find(key, key_type)==sCache.end())) + if(!key.isNull() && !isRequestPending(key) && (refresh || sCache.find(key)==sCache.end())) { - LL_INFOS("ExperienceCache") << " queue request for " << key_type << " " << key << LL_ENDL ; - sAskQueue[key]=key_type; + LL_INFOS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL ; + sAskQueue[key]=EXPERIENCE_ID; return true; } @@ -548,20 +546,20 @@ namespace LLExperienceCache void insert(const LLSD& experience_data ) { - if(experience_data.has(MAP_KEY)) + if(experience_data.has(EXPERIENCE_ID)) { - sCache[experience_data[MAP_KEY].asUUID()]=experience_data; + sCache[experience_data[EXPERIENCE_ID].asUUID()]=experience_data; } else { - LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << MAP_KEY << LL_ENDL; + LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL; } } - bool get( const LLUUID& key, const std::string& key_type, LLSD& experience_data ) + bool get( const LLUUID& key, LLSD& experience_data ) { if(key.isNull()) return false; - cache_t::const_iterator it = Find(key, key_type); + cache_t::const_iterator it = sCache.find(key); if (it != sCache.end()) { @@ -569,17 +567,17 @@ namespace LLExperienceCache return true; } - fetch(key, key_type); + fetch(key); return false; } - void get( const LLUUID& key, const std::string& key_type, callback_slot_t slot ) + void get( const LLUUID& key, callback_slot_t slot ) { if(key.isNull()) return; - cache_t::const_iterator it = Find(key, key_type); + cache_t::const_iterator it = sCache.find(key); if (it != sCache.end()) { // ...name already exists in cache, fire callback now @@ -590,7 +588,7 @@ namespace LLExperienceCache return; } - fetch(key, key_type); + fetch(key); // always store additional callback, even if request is pending signal_map_t::iterator sig_it = sSignalMap.find(key); @@ -610,3 +608,37 @@ namespace LLExperienceCache } } + + + +void LLExperienceCache::mapPrivateKeys( const LLSD& legacyKeys ) +{ + LLSD::array_const_iterator exp = legacyKeys.beginArray(); + for(/**/ ; exp != legacyKeys.endArray() ; ++exp) + { + if(exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY)) + { + experinceKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()]=(*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID(); + } + } +} + + +LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found) +{ + if (private_key.isNull()) + return LLUUID::null; + + + PrivateKeyMap::const_iterator it=experinceKeyMap.find(private_key); + if(it == experinceKeyMap.end()) + { + if(null_if_not_found) + { + return LLUUID::null; + } + return private_key; + } + LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL; + return it->second; +} diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index 396a244935..7a21bd9729 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -39,20 +39,28 @@ class LLUUID; namespace LLExperienceCache { - const std::string PUBLIC_KEY = "public-id"; - const std::string PRIVATE_KEY = "private-id"; - const std::string CREATOR_KEY = "creator-id"; + const std::string PRIVATE_KEY = "private_id"; + + const std::string EXPERIENCE_ID = "public_id"; + const std::string OWNER_ID = "owner_id"; const std::string NAME = "name"; const std::string PROPERTIES = "properties"; const std::string EXPIRES = "expires"; + const std::string DESCRIPTION = "description"; + + // should be in sync with experience-api/experiences/models.py + const int PROPERTY_INVALID = 1 << 0; + const int PROPERTY_NORMAL = 1 << 1; + const int PROPERTY_REGION = 1 << 2; + const int PROPERTY_PRIVILEGED = 1 << 3; + const int PROPERTY_GRID = 1 << 4; + const int PROPERTY_PRIVATE = 1 << 5; + const int PROPERTY_DISABLED = 1 << 6; + const int PROPERTY_SUSPENDED = 1 << 7; + - const int EXPERIENCE_INVALID = (1 << 0); - const int EXPERIENCE_NORMAL = (1 << 1); - const int EXPERIENCE_REGION = (1 << 2); const static F64 DEFAULT_EXPIRATION = 600.0; - // dummy name used when we have nothing else - const std::string DUMMY_NAME = "\?\?\?"; // Callback types for get() below typedef boost::signals2::signal callback_signal_t; @@ -69,16 +77,20 @@ namespace LLExperienceCache void exportFile(std::ostream& ostr); void importFile(std::istream& istr); void initClass(); + void bootstrap(const LLSD& legacyKeys, int initialExpiration); - void erase(const LLUUID& key, const std::string& key_type); - bool fetch(const LLUUID& key, const std::string& key_type, bool refresh = false); + void erase(const LLUUID& key); + bool fetch(const LLUUID& key, bool refresh=false); void insert(LLSD& experience_data); - bool get(const LLUUID& key, const std::string& key_type, LLSD& experience_data); + bool get(const LLUUID& key, LLSD& experience_data); // If name information is in cache, callback will be called immediately. - void get(const LLUUID& key, const std::string& key_type, callback_slot_t slot); + void get(const LLUUID& key, callback_slot_t slot); const cache_t& getCached(); + + LLUUID getExperienceId(const LLUUID& private_key, bool null_if_not_found=false); + }; #endif // LL_LLEXPERIENCECACHE_H diff --git a/indra/newview/llpanelexperiences.cpp b/indra/newview/llpanelexperiences.cpp index 02a3c4c410..617ceef615 100644 --- a/indra/newview/llpanelexperiences.cpp +++ b/indra/newview/llpanelexperiences.cpp @@ -56,7 +56,7 @@ public: { LLUUID public_key = it->asUUID(); - LLExperienceCache::get(public_key, LLExperienceCache::PUBLIC_KEY, boost::bind(ExperienceResult, mParent, _1)); + LLExperienceCache::get(public_key, boost::bind(ExperienceResult, mParent, _1)); } } }; @@ -73,9 +73,9 @@ void LLPanelExperiences::addExperienceInfo(const LLSD& experience) item->setExperienceName(experience["error"].asString()); } - if(experience.has(LLExperienceCache::PUBLIC_KEY)) + if(experience.has(LLExperienceCache::DESCRIPTION)) { - item->setExperienceDescription(experience[LLExperienceCache::PUBLIC_KEY].asString()); + item->setExperienceDescription(experience[LLExperienceCache::DESCRIPTION].asString()); } mExperiencesList->addItem(item); @@ -228,7 +228,7 @@ void LLExperienceItem::init( LLSD* experience_data ) { if(experience_data) { - setExperienceDescription(experience_data->has(LLExperienceCache::PUBLIC_KEY)?(*experience_data)[LLExperienceCache::PUBLIC_KEY].asString() : std::string()); + setExperienceDescription(experience_data->has(LLExperienceCache::DESCRIPTION)?(*experience_data)[LLExperienceCache::DESCRIPTION].asString() : std::string()); setExperienceName(experience_data->has(LLExperienceCache::NAME)?(*experience_data)[LLExperienceCache::NAME].asString() : std::string()); } } diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 98abd2c9dd..ba16c4dde8 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1229,13 +1229,13 @@ public: scriptCore->clearExperiences(); - LLSD experiences = content["experiences"]; + LLSD experiences = content["experience_ids"]; LLSD::array_const_iterator it = experiences.beginArray(); for( /**/ ; it != experiences.endArray(); ++it) { LLUUID public_key = it->asUUID(); - LLExperienceCache::get(public_key, LLExperienceCache::PUBLIC_KEY, boost::bind(AddExperienceResult, mParent, _1)); + LLExperienceCache::get(public_key, boost::bind(AddExperienceResult, mParent, _1)); } } }; @@ -1247,7 +1247,7 @@ void LLScriptEdCore::requestExperiences() LLViewerRegion* region = gAgent.getRegion(); if (region) { - std::string lookup_url=region->getCapability("GetExperiences"); + std::string lookup_url=region->getCapability("GetCreatorExperiences"); if(!lookup_url.empty()) { LLHTTPClient::get(lookup_url, new ExperienceResponder(getDerivedHandle())); @@ -1270,9 +1270,9 @@ void LLScriptEdCore::clearExperiences() LLUUID LLScriptEdCore::getSelectedExperience()const { LLSD value = mExperiences->getSelectedValue(); - if(value.has(LLExperienceCache::PUBLIC_KEY)) + if(value.has(LLExperienceCache::EXPERIENCE_ID)) { - return value[LLExperienceCache::PUBLIC_KEY].asUUID(); + return value[LLExperienceCache::EXPERIENCE_ID].asUUID(); } return LLUUID::null; } diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 70fb5d08e5..351c371994 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1534,6 +1534,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("GetDisplayNames"); capabilityNames.append("GetExperiences"); capabilityNames.append("GetExperienceInfo"); + capabilityNames.append("GetCreatorExperiences"); capabilityNames.append("GetMesh"); capabilityNames.append("GetObjectCost"); capabilityNames.append("GetObjectPhysicsData"); -- cgit v1.3 From d5308c19f7c622538d61e3ebb174338ce95a7b72 Mon Sep 17 00:00:00 2001 From: dolphin Date: Wed, 13 Mar 2013 10:24:30 -0700 Subject: Fixed up expiration code for the experience cache --- indra/llmessage/llexperiencecache.cpp | 52 +++++++++++++++++++---------------- indra/llmessage/llexperiencecache.h | 2 +- 2 files changed, 30 insertions(+), 24 deletions(-) (limited to 'indra/llmessage') diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 1a6e74d123..219e68b51c 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -74,9 +74,9 @@ namespace LLExperienceCache sCache[public_key]=experience; LLSD & row = sCache[public_key]; - if(row.has("expires")) + if(row.has(EXPIRES)) { - row["expires"] = row["expires"].asReal() + LLFrameTimer::getTotalSeconds(); + row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds(); } if(row.has(EXPERIENCE_ID)) @@ -126,9 +126,9 @@ namespace LLExperienceCache LLSD experience = *it; if(experience.has(EXPERIENCE_ID)) { - if(!experience.has("expires")) + if(!experience.has(EXPIRES)) { - experience["expires"] = initialExpiration; + experience[EXPIRES] = initialExpiration; } processExperience(experience[EXPERIENCE_ID].asUUID(), experience); } @@ -154,7 +154,7 @@ namespace LLExperienceCache if (max_age_from_cache_control(cache_control, &max_age)) { LL_WARNS("ExperienceCache") - << "got expiration from headers, max_age " << max_age + << "got EXPIRES from headers, max_age " << max_age << LL_ENDL; F64 now = LLFrameTimer::getTotalSeconds(); *expires = now + (F64)max_age; @@ -251,7 +251,7 @@ namespace LLExperienceCache for( ; it != sCache.end() ; ++it) { if(!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() || - it->second.has("error")) + it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID)) continue; experiences[it->first.asString()] = it->second; @@ -294,17 +294,18 @@ namespace LLExperienceCache } LLSD error_ids = content["error_ids"]; - LLSD::map_const_iterator errIt = error_ids.beginMap(); - for( /**/ ; errIt != error_ids.endMap() ; ++errIt ) + LLSD::array_const_iterator errIt = error_ids.beginArray(); + for( /**/ ; errIt != error_ids.endArray() ; ++errIt ) { - LLUUID id = LLUUID(errIt->first); - for( it = errIt->second.beginArray(); it != errIt->second.endArray() ; ++it) - { - LL_INFOS("ExperienceCache") << "Clearing error result for " << id - << " of type '" << it->asString() << "'" << LL_ENDL ; + LLUUID id = errIt->asUUID(); + LLSD exp; + exp[EXPIRES]=DEFAULT_EXPIRATION; + exp[EXPERIENCE_ID] = id; + exp[PROPERTIES]=PROPERTY_INVALID; + exp["DoesNotExist"]=true; - erase(id); - } + processExperience(id, exp); + LL_INFOS("ExperienceCache") << "Error result for " << id << LL_ENDL ; } LL_INFOS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL; @@ -329,6 +330,7 @@ namespace LLExperienceCache exp["key_type"] = it->second; exp["uuid"] = it->first; exp["error"] = (LLSD::Integer)status; + exp[PROPERTIES]=PROPERTY_INVALID; LLExperienceCache::processExperience(it->first, exp); } @@ -512,20 +514,24 @@ namespace LLExperienceCache cache_t::iterator cur = it; LLSD& exp = cur->second; ++it; - if(exp.has("expires") && exp["expires"].asReal() < now) + if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now) { - if(exp.has("key_type") && exp.has("uuid")) - { - fetch(exp[EXPERIENCE_ID].asUUID(), true); - sCache.erase(cur); - } - else if(exp.has(EXPERIENCE_ID)) + if(exp.has(EXPERIENCE_ID)) { LLUUID id = exp[EXPERIENCE_ID].asUUID(); - if(id.notNull()) + S32 properties = PROPERTY_INVALID; + if(exp.has(PROPERTIES)) + { + properties = exp[PROPERTIES].asInteger(); + } + if(id.notNull() && ((properties & PROPERTY_INVALID) == 0)) { fetch(id, true); } + else + { + sCache.erase(cur); + } } } } diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index 7a21bd9729..1490c978b3 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -45,7 +45,7 @@ namespace LLExperienceCache const std::string OWNER_ID = "owner_id"; const std::string NAME = "name"; const std::string PROPERTIES = "properties"; - const std::string EXPIRES = "expires"; + const std::string EXPIRES = "expiration"; const std::string DESCRIPTION = "description"; // should be in sync with experience-api/experiences/models.py -- cgit v1.3 From 4bdecca57ec82a5dbc323432b7b5cb6aba9c1303 Mon Sep 17 00:00:00 2001 From: dolphin Date: Tue, 2 Apr 2013 15:02:23 -0700 Subject: Removed outdated experience property flags --- indra/llmessage/llexperiencecache.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'indra/llmessage') diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index 1490c978b3..fb00ea31f0 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -50,8 +50,6 @@ namespace LLExperienceCache // should be in sync with experience-api/experiences/models.py const int PROPERTY_INVALID = 1 << 0; - const int PROPERTY_NORMAL = 1 << 1; - const int PROPERTY_REGION = 1 << 2; const int PROPERTY_PRIVILEGED = 1 << 3; const int PROPERTY_GRID = 1 << 4; const int PROPERTY_PRIVATE = 1 << 5; -- cgit v1.3 From 257110c11e5546f0a5a07b087694cfc9059367b4 Mon Sep 17 00:00:00 2001 From: dolphin Date: Wed, 21 Aug 2013 15:43:09 -0700 Subject: Added new fields to the experience cache. Experience profile moved to it's own floater, will probalby be moving again. XP profile displays most data, though the presentation is not final Fixed a bug with the XP selection combobox in the script editor --- indra/llmessage/llexperiencecache.cpp | 61 +++-- indra/llmessage/llexperiencecache.h | 17 +- indra/newview/CMakeLists.txt | 2 + indra/newview/llfloaterexperienceprofile.cpp | 215 +++++++++++++++ indra/newview/llfloaterexperienceprofile.h | 70 +++++ indra/newview/llpreviewscript.cpp | 20 +- indra/newview/llpreviewscript.h | 4 + indra/newview/llviewerfloaterreg.cpp | 6 +- .../default/xui/en/floater_experienceprofile.xml | 293 +++++++++++++++++++++ 9 files changed, 662 insertions(+), 26 deletions(-) create mode 100644 indra/newview/llfloaterexperienceprofile.cpp create mode 100644 indra/newview/llfloaterexperienceprofile.h create mode 100644 indra/newview/skins/default/xui/en/floater_experienceprofile.xml (limited to 'indra/llmessage') diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 219e68b51c..2e260ecdef 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -37,11 +37,10 @@ namespace LLExperienceCache { - typedef std::map PrivateKeyMap; - PrivateKeyMap experinceKeyMap; - - void mapPrivateKeys(const LLSD& legacyKeys); + typedef std::map KeyMap; + KeyMap privateToPublicKeyMap; + void mapKeys(const LLSD& legacyKeys); std::string sLookupURL; @@ -66,6 +65,7 @@ namespace LLExperienceCache signal_map_t sSignalMap; + bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age); void eraseExpired(); @@ -89,6 +89,17 @@ namespace LLExperienceCache sPendingQueue.erase(row[OWNER_ID].asUUID()); } + if(!row.has(OWNER_ID)) + { + if(row.has(AGENT_ID) && row[AGENT_ID].asUUID().notNull()) + { + row[OWNER_ID]=row[AGENT_ID]; + } + else + { + row[OWNER_ID]=row[GROUP_ID]; + } + } //signal signal_map_t::iterator sig_it = sSignalMap.find(public_key); @@ -119,7 +130,7 @@ namespace LLExperienceCache void bootstrap(const LLSD& legacyKeys, int initialExpiration) { - mapPrivateKeys(legacyKeys); + mapKeys(legacyKeys); LLSD::array_const_iterator it = legacyKeys.beginArray(); for(/**/; it != legacyKeys.endArray(); ++it) { @@ -302,7 +313,8 @@ namespace LLExperienceCache exp[EXPIRES]=DEFAULT_EXPIRATION; exp[EXPERIENCE_ID] = id; exp[PROPERTIES]=PROPERTY_INVALID; - exp["DoesNotExist"]=true; + exp[MISSING]=true; + exp[QUOTA] = DEFAULT_QUOTA; processExperience(id, exp); LL_INFOS("ExperienceCache") << "Error result for " << id << LL_ENDL ; @@ -324,14 +336,22 @@ namespace LLExperienceCache ask_queue_t::const_iterator it = mKeys.begin(); for ( ; it != mKeys.end(); ++it) { + LLSD exp; + //leave the properties alone if we already have a cache entry for this xp + if(!get(it->first, exp)) + { + exp[PROPERTIES]=PROPERTY_INVALID; + } exp[EXPIRES]=retry_timestamp; exp[EXPERIENCE_ID] = it->first; exp["key_type"] = it->second; exp["uuid"] = it->first; exp["error"] = (LLSD::Integer)status; - exp[PROPERTIES]=PROPERTY_INVALID; + exp[QUOTA] = DEFAULT_QUOTA; + LLExperienceCache::processExperience(it->first, exp); + LL_INFOS("ExperienceCache") << "Error result for " << it->first << LL_ENDL ; } } @@ -514,22 +534,25 @@ namespace LLExperienceCache cache_t::iterator cur = it; LLSD& exp = cur->second; ++it; + if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now) { - if(exp.has(EXPERIENCE_ID)) + if(!exp.has(EXPERIENCE_ID)) { - LLUUID id = exp[EXPERIENCE_ID].asUUID(); - S32 properties = PROPERTY_INVALID; - if(exp.has(PROPERTIES)) - { - properties = exp[PROPERTIES].asInteger(); + LL_INFOS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ; + sCache.erase(cur); } - if(id.notNull() && ((properties & PROPERTY_INVALID) == 0)) + else + { + LLUUID id = exp[EXPERIENCE_ID].asUUID(); + LLUUID private_key = exp.has(LLExperienceCache::PRIVATE_KEY) ? exp[LLExperienceCache::PRIVATE_KEY].asUUID():LLUUID::null; + if(private_key.notNull() || !exp.has("DoesNotExist")) { fetch(id, true); } else { + LL_INFOS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ; sCache.erase(cur); } } @@ -616,15 +639,14 @@ namespace LLExperienceCache } - -void LLExperienceCache::mapPrivateKeys( const LLSD& legacyKeys ) +void LLExperienceCache::mapKeys( const LLSD& legacyKeys ) { LLSD::array_const_iterator exp = legacyKeys.beginArray(); for(/**/ ; exp != legacyKeys.endArray() ; ++exp) { if(exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY)) { - experinceKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()]=(*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID(); + privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()]=(*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID(); } } } @@ -635,9 +657,8 @@ LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_i if (private_key.isNull()) return LLUUID::null; - - PrivateKeyMap::const_iterator it=experinceKeyMap.find(private_key); - if(it == experinceKeyMap.end()) + KeyMap::const_iterator it=privateToPublicKeyMap.find(private_key); + if(it == privateToPublicKeyMap.end()) { if(null_if_not_found) { diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index fb00ea31f0..8b3443e5a9 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -40,13 +40,21 @@ class LLUUID; namespace LLExperienceCache { const std::string PRIVATE_KEY = "private_id"; + const std::string MISSING = "DoesNotExist"; + const std::string AGENT_ID = "agent_id"; + const std::string GROUP_ID = "group_id"; const std::string EXPERIENCE_ID = "public_id"; const std::string OWNER_ID = "owner_id"; const std::string NAME = "name"; const std::string PROPERTIES = "properties"; const std::string EXPIRES = "expiration"; - const std::string DESCRIPTION = "description"; + const std::string DESCRIPTION = "description"; + const std::string QUOTA = "quota"; + const std::string MATURITY = "maturity"; + const std::string METADATA = "extended_metadata"; + const std::string SLURL = "slurl"; + // should be in sync with experience-api/experiences/models.py const int PROPERTY_INVALID = 1 << 0; @@ -54,10 +62,12 @@ namespace LLExperienceCache const int PROPERTY_GRID = 1 << 4; const int PROPERTY_PRIVATE = 1 << 5; const int PROPERTY_DISABLED = 1 << 6; - const int PROPERTY_SUSPENDED = 1 << 7; + const int PROPERTY_SUSPENDED = 1 << 7; + // default values const static F64 DEFAULT_EXPIRATION = 600.0; + const static S32 DEFAULT_QUOTA = 128; // this is megabytes // Callback types for get() below typedef boost::signals2::signal @@ -79,7 +89,7 @@ namespace LLExperienceCache void erase(const LLUUID& key); bool fetch(const LLUUID& key, bool refresh=false); - void insert(LLSD& experience_data); + void insert(const LLSD& experience_data); bool get(const LLUUID& key, LLSD& experience_data); // If name information is in cache, callback will be called immediately. @@ -87,6 +97,7 @@ namespace LLExperienceCache const cache_t& getCached(); + // maps an experience private key to the experience id LLUUID getExperienceId(const LLUUID& private_key, bool null_if_not_found=false); }; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9caff9e038..1c86884a80 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -227,6 +227,7 @@ set(viewer_SOURCE_FILES llfloatereditwater.cpp llfloaterenvironmentsettings.cpp llfloaterevent.cpp + llfloaterexperienceprofile.cpp llfloaterexperiences.cpp llfloaterfonttest.cpp llfloatergesture.cpp @@ -813,6 +814,7 @@ set(viewer_HEADER_FILES llfloatereditwater.h llfloaterenvironmentsettings.h llfloaterevent.h + llfloaterexperienceprofile.h llfloaterexperiences.h llfloaterfonttest.h llfloatergesture.h diff --git a/indra/newview/llfloaterexperienceprofile.cpp b/indra/newview/llfloaterexperienceprofile.cpp new file mode 100644 index 0000000000..821a1de6f7 --- /dev/null +++ b/indra/newview/llfloaterexperienceprofile.cpp @@ -0,0 +1,215 @@ +/** + * @file llfloaterexperienceprofile.cpp + * @brief llfloaterexperienceprofile and related class definitions + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, 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 "llfloaterexperienceprofile.h" +#include "llexperiencecache.h" +#include "llfloaterworldmap.h" +#include "llfloaterreg.h" +#include "lllayoutstack.h" +#include "lltextbox.h" +#include "llsdserialize.h" +#include "llexpandabletextbox.h" +#include "lltexturectrl.h" +#include "lltrans.h" + +#define XML_PANEL_EXPERIENCE_PROFILE "floater_experienceprofile.xml" +#define TF_NAME "experience_title" +#define TF_DESC "experience_description" +#define TF_SLURL "LocationTextText" +#define TF_MRKT "marketplace" +#define TF_MATURITY "ContentRatingText" +#define TF_OWNER "OwnerText" + +#define IMG_LOGO "logo" + +#define PNL_IMAGE "image_panel" +#define PNL_DESC "description panel" +#define PNL_LOC "location panel" +#define PNL_MRKT "marketplace panel" + +#define BTN_TP "teleport_btn" +#define BTN_MAP "show_on_map_btn" +#define BTN_EDIT "edit_btn" + + +LLFloaterExperienceProfile::LLFloaterExperienceProfile(const LLSD& data) + : LLFloater(data) + , mExperienceId(data.asUUID()) + , mImagePanel(NULL) + , mDescriptionPanel(NULL) + , mLocationPanel(NULL) + , mMarketplacePanel(NULL) +{ + +} + +LLFloaterExperienceProfile::~LLFloaterExperienceProfile() +{ + +} + + +BOOL LLFloaterExperienceProfile::postBuild() +{ + mImagePanel = getChild(PNL_IMAGE); + mDescriptionPanel = getChild(PNL_DESC); + mLocationPanel = getChild(PNL_LOC); + mMarketplacePanel = getChild(PNL_MRKT); + + if (mExperienceId.notNull()) + { + LLExperienceCache::fetch(mExperienceId, true); + LLExperienceCache::get(mExperienceId, boost::bind(&LLFloaterExperienceProfile::experienceCallback, + getDerivedHandle(), _1)); + } + + + childSetAction(BTN_TP, boost::bind(&LLFloaterExperienceProfile::onClickTeleport, this)); + childSetAction(BTN_MAP, boost::bind(&LLFloaterExperienceProfile::onClickMap, this)); + childSetAction(BTN_EDIT, boost::bind(&LLFloaterExperienceProfile::onClickEdit, this)); + + return TRUE; +} + +void LLFloaterExperienceProfile::experienceCallback(LLHandle handle, const LLSD& experience ) +{ + LLFloaterExperienceProfile* pllpep = handle.get(); + if(pllpep) + { + pllpep->refreshExperience(experience); + } +} + +void LLFloaterExperienceProfile::onClickMap() +{ +// LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); +// LLFloaterReg::showInstance("world_map", "center"); + +} + +void LLFloaterExperienceProfile::onClickTeleport() +{ +// if (!getPosGlobal().isExactlyZero()) +// { +// gAgent.teleportViaLocation(getPosGlobal()); +// LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); +// } + +} + +void LLFloaterExperienceProfile::onClickEdit() +{ + +} + +std::string LLFloaterExperienceProfile::getMaturityString(U8 maturity) +{ + if(maturity <= SIM_ACCESS_MIN) + return LLTrans::getString("SIM_ACCESS_MIN"); + if(maturity <= SIM_ACCESS_PG) + return LLTrans::getString("SIM_ACCESS_PG"); + if(maturity <= SIM_ACCESS_MATURE) + return LLTrans::getString("SIM_ACCESS_MATURE"); + if(maturity <= SIM_ACCESS_ADULT) + return LLTrans::getString("SIM_ACCESS_ADULT"); + + return LLStringUtil::null; +} + + +void LLFloaterExperienceProfile::refreshExperience( const LLSD& experience ) +{ + mExperienceDetails = experience; + + if(experience.has(LLExperienceCache::MISSING)) + { + mImagePanel->setVisible(FALSE); + mDescriptionPanel->setVisible(FALSE); + mLocationPanel->setVisible(FALSE); + mMarketplacePanel->setVisible(FALSE); + } + + LLTextBox* child = getChild(TF_NAME); + child->setText(experience[LLExperienceCache::NAME].asString()); + + std::string value = experience[LLExperienceCache::DESCRIPTION].asString(); + LLExpandableTextBox* exchild = getChild(TF_DESC); + exchild->setText(value); + mDescriptionPanel->setVisible(value.length()>0); + + value = experience[LLExperienceCache::SLURL].asString(); + child = getChild(TF_SLURL); + child->setText(value); + mLocationPanel->setVisible(value.length()>0); + + child = getChild(TF_MATURITY); + child->setText(getMaturityString((U8)(experience[LLExperienceCache::MATURITY].asInteger()))); + + child = getChild(TF_OWNER); + child->setText(experience[LLExperienceCache::OWNER_ID].asString()); + + value=experience[LLExperienceCache::METADATA].asString(); + if(value.empty()) + return; + + LLPointer parser = new LLSDXMLParser(); + + LLSD data; + + std::istringstream is = std::istringstream(value); + if(LLSDParser::PARSE_FAILURE != parser->parse(is, data, value.size())) + { + if(data.has(TF_MRKT)) + { + value=data[TF_MRKT].asString(); + + child = getChild(TF_MRKT); + child->setText(value); + mMarketplacePanel->setVisible(TRUE); + } + else + { + mMarketplacePanel->setVisible(FALSE); + } + + if(data.has(IMG_LOGO)) + { + LLTextureCtrl* logo = getChild(IMG_LOGO); + logo->setImageAssetID(data[IMG_LOGO].asUUID()); + mImagePanel->setVisible(TRUE); + } + } + + + + + + + +} diff --git a/indra/newview/llfloaterexperienceprofile.h b/indra/newview/llfloaterexperienceprofile.h new file mode 100644 index 0000000000..1d1e4c50cf --- /dev/null +++ b/indra/newview/llfloaterexperienceprofile.h @@ -0,0 +1,70 @@ +/** + * @file llfloaterexperienceprofile.h + * @brief llfloaterexperienceprofile and related class definitions + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, 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 LL_LLFLOATEREXPERIENCEPROFILE_H +#define LL_LLFLOATEREXPERIENCEPROFILE_H + +#include "llfloater.h" +#include "lluuid.h" +#include "llsd.h" + +class LLLayoutPanel; + +class LLFloaterExperienceProfile : public LLFloater +{ + LOG_CLASS(LLFloaterExperienceProfile); +public: + LLFloaterExperienceProfile(const LLSD& data); + virtual ~LLFloaterExperienceProfile(); + + void setExperienceId( const LLUUID& experience_id ); + +protected: + + void onClickMap(); + void onClickTeleport(); + void onClickEdit(); + + + static void experienceCallback(LLHandle handle, const LLSD& experience); + void refreshExperience(const LLSD& experience); + BOOL postBuild(); + std::string getMaturityString(U8 maturity); + LLUUID mExperienceId; + LLSD mExperienceDetails; + + LLLayoutPanel* mImagePanel; + LLLayoutPanel* mDescriptionPanel; + LLLayoutPanel* mLocationPanel; + LLLayoutPanel* mMarketplacePanel; + +private: + +}; + +#endif // LL_LLFLOATEREXPERIENCEPROFILE_H diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index f5a6ec3dd3..7c2638d82b 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -87,6 +87,7 @@ #include "llviewercontrol.h" #include "llappviewer.h" #include "llexperiencecache.h" +#include "llfloaterexperienceprofile.h" #include "llexperienceassociationresponder.h" const std::string HELLO_LSL = @@ -407,6 +408,22 @@ void LLLiveLSLEditor::experienceChanged() } } +void LLLiveLSLEditor::onViewProfile( LLUICtrl *ui, void* userdata ) +{ + LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; + + LLUUID id; + if(self->mExperienceEnabled->get()) + { + id=self->mScriptEd->getAssociatedExperience(); + if(id.notNull()) + { + LLFloaterReg::showInstance("experience_profile", id, true); + } + } + +} + void LLLiveLSLEditor::onToggleExperience( LLUICtrl *ui, void* userdata ) { LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; @@ -1306,7 +1323,7 @@ void LLLiveLSLEditor::addExperienceInfo(const LLSD& experience, BOOL enabled) void LLLiveLSLEditor::buildExperienceList() { - mExperiences->clear(); + mExperiences->clearRows(); bool foundAssociated=false; for(LLSD::array_const_iterator it = mExperienceIds.beginArray(); it != mExperienceIds.endArray(); ++it) { @@ -1916,6 +1933,7 @@ BOOL LLLiveLSLEditor::postBuild() mExperienceEnabled = getChild("enable_xp"); childSetCommitCallback("enable_xp", onToggleExperience, this); + childSetCommitCallback("view_profile", onViewProfile, this); return LLPreview::postBuild(); diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index faeb4a5b8a..4e76238c71 100755 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -49,6 +49,7 @@ class LLKeywordToken; class LLVFS; class LLViewerInventoryItem; class LLScriptEdContainer; +class LLFloaterExperienceProfile; // Inner, implementation class. LLPreviewScript and LLLiveLSLEditor each own one of these. class LLScriptEdCore : public LLPanel @@ -234,6 +235,7 @@ public: static void setAssociatedExperience( LLHandle editor, const LLSD& experience ); static void onToggleExperience(LLUICtrl *ui, void* userdata); + static void onViewProfile(LLUICtrl *ui, void* userdata); void addExperienceInfo( const LLSD& experience, BOOL enabled ); void setExperienceIds(const LLSD& experience_ids); @@ -300,6 +302,8 @@ private: LLComboBox *mExperiences; LLCheckBoxCtrl *mExperienceEnabled; LLSD mExperienceIds; + + LLHandle mExperienceProfile; }; #endif // LL_LLPREVIEWSCRIPT_H diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index c6296a20d7..13ceaf491c 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -58,6 +58,7 @@ #include "llfloatereditsky.h" #include "llfloatereditwater.h" #include "llfloaterenvironmentsettings.h" +#include "llfloaterexperienceprofile.h" #include "llfloaterexperiences.h" #include "llfloaterevent.h" #include "llfloaterdestinations.h" @@ -207,8 +208,9 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("env_edit_water", "floater_edit_water_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("env_edit_day_cycle", "floater_edit_day_cycle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("experiences", "floater_experiences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("experiences", "floater_experiences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("experience_profile", "floater_experienceprofile.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("font_test", "floater_font_test.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/skins/default/xui/en/floater_experienceprofile.xml b/indra/newview/skins/default/xui/en/floater_experienceprofile.xml new file mode 100644 index 0000000000..d9990f3a59 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_experienceprofile.xml @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + + + + + Location: + + + someplace + +