From d51bd95a1f9cdeae22f330389c5213a1093eb971 Mon Sep 17 00:00:00 2001 From: "dolphin@dolphin-THINK.home" Date: Tue, 13 Nov 2012 08:24:18 -0800 Subject: Added a simple ui for displaying experiences keys with hardcoded contents. --- indra/newview/CMakeLists.txt | 4 + indra/newview/llfloaterexperiences.cpp | 14 ++ indra/newview/llfloaterexperiences.h | 45 ++++ indra/newview/llpanelexperiences.cpp | 251 +++++++++++++++++++++ indra/newview/llpanelexperiences.h | 124 ++++++++++ indra/newview/llviewerfloaterreg.cpp | 4 +- .../skins/default/xui/en/floater_experiences.xml | 29 +++ indra/newview/skins/default/xui/en/menu_viewer.xml | 8 + .../skins/default/xui/en/panel_experience_info.xml | 79 +++++++ .../skins/default/xui/en/panel_experiences.xml | 40 ++++ 10 files changed, 597 insertions(+), 1 deletion(-) create mode 100644 indra/newview/llfloaterexperiences.cpp create mode 100644 indra/newview/llfloaterexperiences.h create mode 100644 indra/newview/llpanelexperiences.cpp create mode 100644 indra/newview/llpanelexperiences.h create mode 100644 indra/newview/skins/default/xui/en/floater_experiences.xml create mode 100644 indra/newview/skins/default/xui/en/panel_experience_info.xml create mode 100644 indra/newview/skins/default/xui/en/panel_experiences.xml (limited to 'indra') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ccf4ce8d1f..039f243323 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -199,6 +199,7 @@ set(viewer_SOURCE_FILES llfloatereditwater.cpp llfloaterenvironmentsettings.cpp llfloaterevent.cpp + llfloaterexperiences.cpp llfloaterfonttest.cpp llfloatergesture.cpp llfloatergodtools.cpp @@ -367,6 +368,7 @@ set(viewer_SOURCE_FILES llpanelclassified.cpp llpanelcontents.cpp llpaneleditwearable.cpp + llpanelexperiences.cpp llpanelface.cpp llpanelgenerictip.cpp llpanelgroup.cpp @@ -775,6 +777,7 @@ set(viewer_HEADER_FILES llfloatereditwater.h llfloaterenvironmentsettings.h llfloaterevent.h + llfloaterexperiences.h llfloaterfonttest.h llfloatergesture.h llfloatergodtools.h @@ -937,6 +940,7 @@ set(viewer_HEADER_FILES llpanelclassified.h llpanelcontents.h llpaneleditwearable.h + llpanelexperiences.h llpanelface.h llpanelgenerictip.h llpanelgroup.h diff --git a/indra/newview/llfloaterexperiences.cpp b/indra/newview/llfloaterexperiences.cpp new file mode 100644 index 0000000000..b862b41bba --- /dev/null +++ b/indra/newview/llfloaterexperiences.cpp @@ -0,0 +1,14 @@ +#include "llviewerprecompiledheaders.h" + +#include "llpanelexperiences.h" +#include "llfloaterexperiences.h" + +LLFloaterExperiences::LLFloaterExperiences(const LLSD& data) + :LLFloater(data) +{ +} + +BOOL LLFloaterExperiences::postBuild() +{ + return TRUE; +} diff --git a/indra/newview/llfloaterexperiences.h b/indra/newview/llfloaterexperiences.h new file mode 100644 index 0000000000..1e5f216f8d --- /dev/null +++ b/indra/newview/llfloaterexperiences.h @@ -0,0 +1,45 @@ +/** + * @file llfloaterexperiences.h + * @brief LLFloaterExperiences class definition + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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_LLFLOATEREXPERIENCES_H +#define LL_LLFLOATEREXPERIENCES_H + +#include "llfloater.h" + +class LLFloaterExperiences : + public LLFloater +{ +public: + LLFloaterExperiences(const LLSD& data); + +protected: + /*virtual*/ BOOL postBuild(); + +private: + +}; + +#endif //LL_LLFLOATEREXPERIENCES_H diff --git a/indra/newview/llpanelexperiences.cpp b/indra/newview/llpanelexperiences.cpp new file mode 100644 index 0000000000..f174171a97 --- /dev/null +++ b/indra/newview/llpanelexperiences.cpp @@ -0,0 +1,251 @@ +#include "llviewerprecompiledheaders.h" + + +#include "llpanelprofile.h" +#include "lluictrlfactory.h" + +#include "llpanelexperiences.h" + + +static LLRegisterPanelClassWrapper register_experiences_panel("experiences_panel"); + + +LLPanelExperiences::LLPanelExperiences( ) + : mExperiencesList(NULL), + mExperiencesAccTab(NULL), + mProfilePanel(NULL), + mPanelExperienceInfo(NULL), + mNoExperiences(false) +{ + +} + +void* LLPanelExperiences::create( void* data ) +{ + return new LLPanelExperiences(); +} + + +BOOL LLPanelExperiences::postBuild( void ) +{ + mExperiencesList = getChild("experiences_list"); + if(hasString("no_experiences")) + { + mExperiencesList->setNoItemsCommentText(getString("no_experiences")); + } + + LLExperienceItem* item = new LLExperienceItem(); + item->setExperienceName("experience 1"); + item->setExperienceDescription("hey, I\'m an experience!"); + mExperiencesList->addItem(item); + + item = new LLExperienceItem(); + item->setExperienceName("experience 2"); + item->setExperienceDescription("hey, I\'m another experience!"); + mExperiencesList->addItem(item); + + mExperiencesAccTab = getChild("tab_experiences"); + mExperiencesAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelExperiences::onAccordionStateChanged, this, mExperiencesAccTab)); + mExperiencesAccTab->setDisplayChildren(true); + + return TRUE; +} + +void LLPanelExperiences::onOpen( const LLSD& key ) +{ + LLPanel::onOpen(key); +} + +void LLPanelExperiences::onClosePanel() +{ + if (mPanelExperienceInfo) + { + onPanelExperienceClose(mPanelExperienceInfo); + } +} + +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()); + */ + } +} + +LLExperienceItem* LLPanelExperiences::getSelectedExperienceItem() +{ + LLPanel* selected_item = mExperiencesList->getSelectedItem(); + if (!selected_item) return NULL; + + return dynamic_cast(selected_item); +} + +void LLPanelExperiences::setProfilePanel( LLPanelProfile* profile_panel ) +{ + mProfilePanel = profile_panel; +} + +void LLPanelExperiences::onListCommit( const LLFlatListView* f_list ) +{ + if(f_list == mExperiencesList) + { + mExperiencesList->resetSelection(true); + } + else + { + llwarns << "Unknown list" << llendl; + } + + //updateButtons(); +} + +void LLPanelExperiences::onAccordionStateChanged( const LLAccordionCtrlTab* acc_tab ) +{ + if(!mExperiencesAccTab->getDisplayChildren()) + { + mExperiencesList->resetSelection(true); + } + +} + +void LLPanelExperiences::openExperienceInfo() +{ + LLSD selected_value = mExperiencesList->getSelectedValue(); + if(selected_value.isUndefined()) + { + return; + } + + LLExperienceItem* experience = (LLExperienceItem*)mExperiencesList->getSelectedItem(); + + createExperienceInfoPanel(); + + LLSD params; + params["experience_name"] = experience->getExperienceName(); + params["experience_desc"] = experience->getExperienceDescription(); + + getProfilePanel()->openPanel(mPanelExperienceInfo, params); + +} + + +void LLPanelExperiences::createExperienceInfoPanel() +{ + if(!mPanelExperienceInfo) + { + mPanelExperienceInfo = LLPanelExperienceInfo::create(); + mPanelExperienceInfo->setExitCallback(boost::bind(&LLPanelExperiences::onPanelExperienceClose, this, mPanelExperienceInfo)); + mPanelExperienceInfo->setVisible(FALSE); + } +} + +void LLPanelExperiences::onPanelExperienceClose( LLPanel* panel ) +{ + getProfilePanel()->closePanel(panel); +} + +LLPanelProfile* LLPanelExperiences::getProfilePanel() +{ + llassert_always(NULL != mProfilePanel); + + return mProfilePanel; +} + + + + + + + + + + + +LLExperienceItem::LLExperienceItem() +{ + buildFromFile("panel_experience_info.xml"); +} + +void LLExperienceItem::init( LLExperienceData* experience_data ) +{ + +} + +void LLExperienceItem::setExperienceDescription( const std::string& val ) +{ + mExperienceDescription = val; + getChild("experience_desc")->setValue(val); +} + +void LLExperienceItem::setExperienceName( const std::string& val ) +{ + mExperienceName = val; + getChild("experience_name")->setValue(val); +} + +BOOL LLExperienceItem::postBuild() +{ + return TRUE; +} + +void LLExperienceItem::update() +{ + +} + +void LLExperienceItem::processProperties( void* data, EAvatarProcessorType type ) +{ + +} + +LLExperienceItem::~LLExperienceItem() +{ + +} + + +void LLPanelExperienceInfo::setExperienceName( const std::string& name ) +{ + getChild("experience_name")->setValue(name); +} + +void LLPanelExperienceInfo::setExperienceDesc( const std::string& desc ) +{ + getChild("experience_desc")->setValue(desc); +} + +void LLPanelExperienceInfo::onOpen( const LLSD& key ) +{ + setExperienceName(key["experience_name"]); + setExperienceDesc(key["experience_desc"]); + + /* + LLAvatarPropertiesProcessor::getInstance()->addObserver( + getAvatarId(), this); + LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest( + getAvatarId(), getPickId()); + */ +} + +LLPanelExperienceInfo* LLPanelExperienceInfo::create() +{ + LLPanelExperienceInfo* panel = new LLPanelExperienceInfo(); + panel->buildFromFile("panel_experience_info.xml"); + return panel; +} + +void LLPanelExperienceInfo::setExitCallback( const commit_callback_t& cb ) +{ + getChild("back_btn")->setClickedCallback(cb); +} diff --git a/indra/newview/llpanelexperiences.h b/indra/newview/llpanelexperiences.h new file mode 100644 index 0000000000..2a0f101f8a --- /dev/null +++ b/indra/newview/llpanelexperiences.h @@ -0,0 +1,124 @@ +/** + * @file llpanelpicks.h + * @brief LLPanelPicks and related class definitions + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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_LLPANELEXPERIENCES_H +#define LL_LLPANELEXPERIENCES_H + +#include "llaccordionctrltab.h" +#include "llflatlistview.h" +#include "llpanelavatar.h" + +class LLExperienceItem; +class LLPanelProfile; + +class LLPanelExperienceInfo + : public LLPanel +{ +public: + static LLPanelExperienceInfo* create(); + + void onOpen(const LLSD& key); + void setExperienceName( const std::string& name ); + void setExperienceDesc( const std::string& desc ); + + + virtual void setExitCallback(const commit_callback_t& cb); +}; + + +class LLPanelExperiences + : public LLPanel /*LLPanelProfileTab*/ +{ +public: + LLPanelExperiences(); + + static void* create(void* data); + + /*virtual*/ BOOL postBuild(void); + + /*virtual*/ void onOpen(const LLSD& key); + + /*virtual*/ void onClosePanel(); + + void updateData(); + + LLExperienceItem* getSelectedExperienceItem(); + + void setProfilePanel(LLPanelProfile* profile_panel); + +protected: + + void onListCommit(const LLFlatListView* f_list); + void onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab); + + + void openExperienceInfo(); + void createExperienceInfoPanel(); + void onPanelExperienceClose(LLPanel* panel); + LLPanelProfile* getProfilePanel(); +private: + LLFlatListView* mExperiencesList; + LLAccordionCtrlTab* mExperiencesAccTab; + LLPanelProfile* mProfilePanel; + LLPanelExperienceInfo* mPanelExperienceInfo; + bool mNoExperiences; +}; + +struct LLExperienceData +{ + std::string name; + std::string desc; +}; + +class LLExperienceItem + : public LLPanel + //, public LLAvatarPropertiesObserver +{ +public: + LLExperienceItem(); + ~LLExperienceItem(); + + void init(LLExperienceData* experience_data); + /*virtual*/ BOOL postBuild(); + void update(); + + /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); + + void setCreatorID(const LLUUID& val) { mCreatorID = val; } + void setExperienceDescription(const std::string& val); + void setExperienceName(const std::string& val); + + const LLUUID& getCreatorID() const { return mCreatorID; } + const std::string& getExperienceName() const { return mExperienceName; } + const std::string& getExperienceDescription() const { return mExperienceDescription; } + +protected: + LLUUID mCreatorID; + + std::string mExperienceName; + std::string mExperienceDescription; +}; +#endif // LL_LLPANELEXPERIENCES_H diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 1f7cf0cdd4..5582d256f8 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -56,6 +56,7 @@ #include "llfloatereditsky.h" #include "llfloatereditwater.h" #include "llfloaterenvironmentsettings.h" +#include "llfloaterexperiences.h" #include "llfloaterevent.h" #include "llfloaterdestinations.h" #include "llfloaterfonttest.h" @@ -204,7 +205,8 @@ void LLViewerFloaterReg::registerFloaters() 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("font_test", "floater_font_test.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("gestures", "floater_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/skins/default/xui/en/floater_experiences.xml b/indra/newview/skins/default/xui/en/floater_experiences.xml new file mode 100644 index 0000000000..57541c8b35 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_experiences.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 1aa55acf2d..36b1cea701 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -52,6 +52,14 @@ function="Inventory.NewWindow" parameter="" /> + + + diff --git a/indra/newview/skins/default/xui/en/panel_experience_info.xml b/indra/newview/skins/default/xui/en/panel_experience_info.xml new file mode 100644 index 0000000000..47f366d857 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_experience_info.xml @@ -0,0 +1,79 @@ + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/panel_experiences.xml b/indra/newview/skins/default/xui/en/panel_experiences.xml new file mode 100644 index 0000000000..47a3005aae --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_experiences.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + -- cgit v1.2.3 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 +++++- indra/newview/llappviewer.cpp | 50 +++++++++++- indra/newview/llappviewer.h | 5 ++ indra/newview/llpanelexperiences.cpp | 23 +++--- indra/newview/llpanelexperiences.h | 6 +- indra/newview/llstartup.cpp | 12 +++ indra/newview/llstartup.h | 1 + indra/newview/llvoavatar.cpp | 4 + 9 files changed, 231 insertions(+), 29 deletions(-) (limited to 'indra') 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 diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index a674ab0cf8..b80202bd45 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -100,6 +100,7 @@ // Linden library includes #include "llavatarnamecache.h" #include "lldiriterator.h" +#include "llexperiencecache.h" #include "llimagej2c.h" #include "llmemory.h" #include "llprimitive.h" @@ -4145,7 +4146,7 @@ void LLAppViewer::loadNameCache() } void LLAppViewer::saveNameCache() - { +{ // display names cache std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); @@ -4153,7 +4154,7 @@ void LLAppViewer::saveNameCache() if(name_cache_stream.is_open()) { LLAvatarNameCache::exportFile(name_cache_stream); -} + } if (!gCacheName) return; @@ -4166,6 +4167,32 @@ void LLAppViewer::saveNameCache() } } + +void LLAppViewer::saveExperienceCache() +{ + std::string filename = + gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml"); + LL_INFOS("ExperienceCache") << "Saving " << filename << LL_ENDL; + llofstream cache_stream(filename); + if(cache_stream.is_open()) + { + LLExperienceCache::exportFile(cache_stream); + } +} + +void LLAppViewer::loadExperienceCache() +{ + std::string filename = + gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml"); + LL_INFOS("ExperienceCache") << "Loading " << filename << LL_ENDL; + llifstream cache_stream(filename); + if(cache_stream.is_open()) + { + LLExperienceCache::importFile(cache_stream); + } +} + + /*! @brief This class is an LLFrameTimer that can be created with an elapsed time that starts counting up from the given value rather than 0.0. @@ -4368,7 +4395,7 @@ void LLAppViewer::idle() // floating throughout the various object lists. // idleNameCache(); - + idleExperienceCache(); idleNetwork(); @@ -4782,6 +4809,22 @@ void LLAppViewer::idleNameCache() LLAvatarNameCache::idle(); } +void LLAppViewer::idleExperienceCache() +{ + LLViewerRegion* region = gAgent.getRegion(); + if (!region) return; + + std::string lookup_url=region->getCapability("GetDisplayNames"); // use GetDisplayNames for testing round trip + if(!lookup_url.empty() && lookup_url.back() != '/') + { + lookup_url += '/'; + } + + LLExperienceCache::setLookupURL(lookup_url); + + LLExperienceCache::idle(); +} + // // Handle messages, and all message related stuff // @@ -4945,6 +4988,7 @@ void LLAppViewer::disconnectViewer() } saveNameCache(); + saveExperienceCache(); // close inventory interface, close all windows LLFloaterInventory::cleanup(); diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 7a474f9122..4bf6bec0e8 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -114,6 +114,10 @@ public: void loadNameCache(); void saveNameCache(); + void loadExperienceCache(); + void saveExperienceCache(); + + void removeMarkerFile(bool leave_logout_marker = false); // LLAppViewer testing helpers. @@ -218,6 +222,7 @@ private: void idle(); void idleShutdown(); // update avatar SLID and display name caches + void idleExperienceCache(); void idleNameCache(); void idleNetwork(); diff --git a/indra/newview/llpanelexperiences.cpp b/indra/newview/llpanelexperiences.cpp index f174171a97..bc9d3cc0ee 100644 --- a/indra/newview/llpanelexperiences.cpp +++ b/indra/newview/llpanelexperiences.cpp @@ -3,6 +3,7 @@ #include "llpanelprofile.h" #include "lluictrlfactory.h" +#include "llexperiencecache.h" #include "llpanelexperiences.h" @@ -34,15 +35,16 @@ BOOL LLPanelExperiences::postBuild( void ) mExperiencesList->setNoItemsCommentText(getString("no_experiences")); } - LLExperienceItem* item = new LLExperienceItem(); - item->setExperienceName("experience 1"); - item->setExperienceDescription("hey, I\'m an experience!"); - mExperiencesList->addItem(item); - - item = new LLExperienceItem(); - item->setExperienceName("experience 2"); - item->setExperienceDescription("hey, I\'m another experience!"); - mExperiencesList->addItem(item); + const LLExperienceCache::cache_t& experiences = LLExperienceCache::getCached(); + + LLExperienceCache::cache_t::const_iterator it = experiences.begin(); + for( ; it != experiences.end() && mExperiencesList->getChildCount() < 10 ; ++it) + { + LLExperienceItem* item = new LLExperienceItem(); + item->setExperienceName(it->second.mDisplayName); + item->setExperienceDescription(it->second.mDescription); + mExperiencesList->addItem(item); + } mExperiencesAccTab = getChild("tab_experiences"); mExperiencesAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelExperiences::onAccordionStateChanged, this, mExperiencesAccTab)); @@ -179,7 +181,8 @@ LLExperienceItem::LLExperienceItem() void LLExperienceItem::init( LLExperienceData* experience_data ) { - + setExperienceDescription(experience_data->mDescription); + setExperienceName(experience_data->mDisplayName); } void LLExperienceItem::setExperienceDescription( const std::string& val ) diff --git a/indra/newview/llpanelexperiences.h b/indra/newview/llpanelexperiences.h index 2a0f101f8a..33bb0f944a 100644 --- a/indra/newview/llpanelexperiences.h +++ b/indra/newview/llpanelexperiences.h @@ -31,6 +31,7 @@ #include "llflatlistview.h" #include "llpanelavatar.h" +class LLExperienceData; class LLExperienceItem; class LLPanelProfile; @@ -87,11 +88,6 @@ private: bool mNoExperiences; }; -struct LLExperienceData -{ - std::string name; - std::string desc; -}; class LLExperienceItem : public LLPanel diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 42648b82c2..7b12d5509e 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -47,6 +47,7 @@ #include "llares.h" #include "llavatarnamecache.h" +#include "llexperiencecache.h" #include "lllandmark.h" #include "llcachename.h" #include "lldir.h" @@ -1398,6 +1399,9 @@ bool idle_startup() LLStartUp::initNameCache(); display_startup(); + LLStartUp::initExperienceCache(); + display_startup(); + // update the voice settings *after* gCacheName initialization // so that we can construct voice UI that relies on the name cache LLVoiceClient::getInstance()->updateSettings(); @@ -2809,6 +2813,13 @@ void LLStartUp::initNameCache() LLAvatarNameCache::setUseDisplayNames(gSavedSettings.getBOOL("UseDisplayNames")); } + +void LLStartUp::initExperienceCache() +{ + LLAppViewer::instance()->loadExperienceCache(); + LLExperienceCache::initClass(false); +} + void LLStartUp::cleanupNameCache() { LLAvatarNameCache::cleanupClass(); @@ -3507,3 +3518,4 @@ void transition_back_to_login_panel(const std::string& emsg) reset_login(); // calls LLStartUp::setStartupState( STATE_LOGIN_SHOW ); gSavedSettings.setBOOL("AutoLogin", FALSE); } + diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 760e38890b..00e03bcda6 100755 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -91,6 +91,7 @@ public: static void fontInit(); static void initNameCache(); + static void initExperienceCache(); static void cleanupNameCache(); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 627238b0f5..4635138fa3 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -48,6 +48,7 @@ #include "llanimationstates.h" #include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" +#include "llexperiencecache.h" #include "llphysicsmotion.h" #include "llviewercontrol.h" #include "llcallingcard.h" // IDEVO for LLAvatarTracker @@ -2516,6 +2517,9 @@ 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.2.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') 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.2.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') 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.2.3 From 596522c7e5a9bff53a56db90597c7bcbcdbf1537 Mon Sep 17 00:00:00 2001 From: dolphin Date: Tue, 29 Jan 2013 13:58:13 -0800 Subject: Added a combox to the script editor to select an experience. Selected experience is sent with the script when saved. The list of experiences is currently populated with the avatar's current experience, if any. --- indra/newview/llpreviewscript.cpp | 105 +++++++++++++++++++-- indra/newview/llpreviewscript.h | 13 ++- .../skins/default/xui/en/panel_script_ed.xml | 9 ++ 3 files changed, 115 insertions(+), 12 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 9c25e69db0..98abd2c9dd 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -86,6 +86,7 @@ #include "lltrans.h" #include "llviewercontrol.h" #include "llappviewer.h" +#include "llexperiencecache.h" const std::string HELLO_LSL = "default\n" @@ -370,12 +371,21 @@ LLScriptEdCore::~LLScriptEdCore() delete mLiveFile; } +void LLScriptEdCore::experienceChanged() +{ + enableSave(TRUE); + getChildView("Save_btn")->setEnabled(true); +} + BOOL LLScriptEdCore::postBuild() { mErrorList = getChild("lsl errors"); mFunctions = getChild( "Insert..."); + mExperiences = getChild("Experiences..."); + mExperiences->setCommitCallback(boost::bind(&LLScriptEdCore::experienceChanged, this)); + childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this); mEditor = getChild("Script Editor"); @@ -385,6 +395,10 @@ BOOL LLScriptEdCore::postBuild() childSetAction("Edit_btn", boost::bind(&LLScriptEdCore::openInExternalEditor, this)); initMenu(); + + + + requestExperiences(); std::vector funcs; @@ -1187,6 +1201,86 @@ bool LLScriptEdCore::enableLoadFromFileMenu(void* userdata) return (self && self->mEditor) ? self->mEditor->canLoadOrSaveToFile() : FALSE; } + +void AddExperienceResult(LLHandle panel, const LLSD& experience) +{ + LLScriptEdCore* scriptCore = panel.get(); + if(scriptCore) + { + scriptCore->addExperienceInfo(experience); + } +} + + +class ExperienceResponder : public LLHTTPClient::Responder +{ +public: + ExperienceResponder(const LLHandle& parent):mParent(parent) + { + } + + LLHandle mParent; + + virtual void result(const LLSD& content) + { + LLScriptEdCore* scriptCore = mParent.get(); + if(!scriptCore) + return; + + scriptCore->clearExperiences(); + + 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(AddExperienceResult, mParent, _1)); + } + } +}; + +void LLScriptEdCore::requestExperiences() +{ + mExperiences->setEnabled(FALSE); + + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string lookup_url=region->getCapability("GetExperiences"); + if(!lookup_url.empty()) + { + LLHTTPClient::get(lookup_url, new ExperienceResponder(getDerivedHandle())); + } + } +} + +void LLScriptEdCore::addExperienceInfo( const LLSD& experience ) +{ + mExperiences->setEnabled(TRUE); + mExperiences->add(experience[LLExperienceCache::NAME], experience); +} + +void LLScriptEdCore::clearExperiences() +{ + mExperiences->removeall(); + mExperiences->add("No Experience"); +} + +LLUUID LLScriptEdCore::getSelectedExperience()const +{ + LLSD value = mExperiences->getSelectedValue(); + if(value.has(LLExperienceCache::PUBLIC_KEY)) + { + return value[LLExperienceCache::PUBLIC_KEY].asUUID(); + } + return LLUUID::null; +} + + + + + /// --------------------------------------------------------------------------- /// LLScriptEdContainer /// --------------------------------------------------------------------------- @@ -2142,8 +2236,8 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) mPendingUploads++; BOOL is_running = getChild( "running")->get(); if (!url.empty()) - { - uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running); + { + uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running, mScriptEd->getSelectedExperience()); } else if (gAssetStorage) { @@ -2151,11 +2245,7 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) } } -void LLLiveLSLEditor::uploadAssetViaCaps(const std::string& url, - const std::string& filename, - const LLUUID& task_id, - const LLUUID& item_id, - BOOL is_running) +void LLLiveLSLEditor::uploadAssetViaCaps( const std::string& url, const std::string& filename, const LLUUID& task_id, const LLUUID& item_id, BOOL is_running, const LLUUID& experience_public_id ) { llinfos << "Update Task Inventory via capability " << url << llendl; LLSD body; @@ -2163,6 +2253,7 @@ void LLLiveLSLEditor::uploadAssetViaCaps(const std::string& url, body["item_id"] = item_id; body["is_script_running"] = is_running; body["target"] = monoChecked() ? "mono" : "lsl2"; + body["experience"] = experience_public_id; LLHTTPClient::post(url, body, new LLUpdateTaskInventoryResponder(body, filename, LLAssetType::AT_LSL_TEXT)); } diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 7563cecd9d..27252d17a1 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -106,6 +106,9 @@ public: static bool enableLoadFromFileMenu(void* userdata); virtual bool hasAccelerators() const { return true; } + void addExperienceInfo( const LLSD& experience ); + void clearExperiences(); + LLUUID getSelectedExperience()const; private: void onBtnHelp(); @@ -120,6 +123,9 @@ private: void enableSave(BOOL b) {mEnableSave = b;} + void requestExperiences(); + void experienceChanged(); + protected: void deleteBridges(); void setHelpPage(const std::string& help_string); @@ -135,6 +141,7 @@ private: void (*mSearchReplaceCallback) (void* userdata); void* mUserdata; LLComboBox *mFunctions; + LLComboBox *mExperiences; BOOL mForceClose; LLPanel* mCodePanel; LLScrollListCtrl* mErrorList; @@ -237,11 +244,7 @@ private: virtual void loadAsset(); void loadAsset(BOOL is_new); /*virtual*/ void saveIfNeeded(bool sync = true); - void uploadAssetViaCaps(const std::string& url, - const std::string& filename, - const LLUUID& task_id, - const LLUUID& item_id, - BOOL is_running); + void uploadAssetViaCaps(const std::string& url, const std::string& filename, const LLUUID& task_id, const LLUUID& item_id, BOOL is_running, const LLUUID& experience_public_id); void uploadAssetLegacy(const std::string& filename, LLViewerObject* object, const LLTransactionID& tid, diff --git a/indra/newview/skins/default/xui/en/panel_script_ed.xml b/indra/newview/skins/default/xui/en/panel_script_ed.xml index 765b07ed8b..7e4ac1d7fb 100644 --- a/indra/newview/skins/default/xui/en/panel_script_ed.xml +++ b/indra/newview/skins/default/xui/en/panel_script_ed.xml @@ -204,4 +204,13 @@ right="400" name="Edit_btn" width="81" /> + -- cgit v1.2.3 From b03918c73d47290597bdf101d9b013ebfab1f95f Mon Sep 17 00:00:00 2001 From: dolphin Date: Mon, 11 Feb 2013 11:57:42 -0800 Subject: removed reference to std::string::back for linuxy compilation --- indra/newview/llappviewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 262175e009..3a218b58da 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4812,7 +4812,7 @@ void LLAppViewer::idleExperienceCache() if (!region) return; std::string lookup_url=region->getCapability("GetExperienceInfo"); - if(!lookup_url.empty() && lookup_url.back() != '/') + if(!lookup_url.empty() && *lookup_url.rbegin() != '/') { lookup_url += '/'; } -- cgit v1.2.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') 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.2.3 From 2d9ad6ba687cacc81251e1ce57e323b0c2782db9 Mon Sep 17 00:00:00 2001 From: dolphin Date: Wed, 13 Mar 2013 08:53:51 -0700 Subject: Added group roles for experiences and updated the roles management UI to access them. --- indra/llcommon/roles_constants.h | 5 ++++- indra/newview/skins/default/xui/en/role_actions.xml | 12 ++++++++++++ indra/newview/skins/default/xui/en/strings.xml | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llcommon/roles_constants.h b/indra/llcommon/roles_constants.h index effd15ea72..65ec290200 100644 --- a/indra/llcommon/roles_constants.h +++ b/indra/llcommon/roles_constants.h @@ -53,7 +53,7 @@ enum LLRoleChangeType // KNOWN HOLES: use these for any single bit powers you need // bit 0x1 << 46 -// bit 0x1 << 49 and above +// bit 0x1 << 51 and above // These powers were removed to make group roles simpler // bit 0x1 << 41 (GP_ACCOUNTING_VIEW) @@ -146,6 +146,9 @@ const U64 GP_SESSION_JOIN = 0x1LL << 16; //can join session const U64 GP_SESSION_VOICE = 0x1LL << 27; //can hear/talk const U64 GP_SESSION_MODERATOR = 0x1LL << 37; //can mute people's session +const U64 GP_EXPERIENCE_ADMIN = 0x1LL << 49; // has admin rights to any experiences owned by this group +const U64 GP_EXPERIENCE_CREATOR = 0x1LL << 50; // can sign scripts for experiences owned by this group + const U64 GP_DEFAULT_MEMBER = GP_ACCOUNTING_ACCOUNTABLE | GP_LAND_ALLOW_SET_HOME | GP_NOTICES_RECEIVE diff --git a/indra/newview/skins/default/xui/en/role_actions.xml b/indra/newview/skins/default/xui/en/role_actions.xml index 89aef57cca..65acc14b6a 100644 --- a/indra/newview/skins/default/xui/en/role_actions.xml +++ b/indra/newview/skins/default/xui/en/role_actions.xml @@ -187,4 +187,16 @@ longdescription="Members in a Role with this Ability can control access and participation in group voice and text chat sessions." name="moderate group chat" value="37" /> + + + + diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 4383b98592..2e1e740d6f 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3907,4 +3907,7 @@ Try enclosing path to the editor with double quotes. [User] + + Experience + -- cgit v1.2.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') 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.2.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') 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.2.3 From 6308f93c6adddc9d396e8d2527e97e0d02c4aa52 Mon Sep 17 00:00:00 2001 From: dolphin Date: Mon, 20 May 2013 10:04:29 -0700 Subject: WIP check in, testing GetMeta --- indra/newview/llpreviewscript.cpp | 71 ++++++++++++++++++++++++++++++++++++--- indra/newview/llpreviewscript.h | 11 +++--- indra/newview/llviewerregion.cpp | 3 +- 3 files changed, 76 insertions(+), 9 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index f39a5ffd62..59a206b4d1 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1244,6 +1244,33 @@ public: } }; +class ExperienceAssociationResponder : public LLHTTPClient::Responder +{ +public: + ExperienceAssociationResponder(const LLUUID& parent):mParent(parent) + { + } + + LLUUID mParent; + + virtual void result(const LLSD& content) + { + + LLLiveLSLEditor* scriptCore = LLFloaterReg::findTypedInstance("preview_scriptedit", mParent); + + if(!scriptCore || !content.has("experience")) + return; + + scriptCore->setAssociatedExperience(content["experience"].asUUID()); + } + + virtual void error(U32 status, const std::string& reason) + { + llinfos << "Failed to look up associated script: " << status << ": " << reason << llendl; + } + +}; + void LLScriptEdCore::requestExperiences() { mExperiences->setEnabled(FALSE); @@ -1262,13 +1289,13 @@ void LLScriptEdCore::requestExperiences() void LLScriptEdCore::addExperienceInfo( const LLSD& experience ) { mExperiences->setEnabled(TRUE); - mExperiences->add(experience[LLExperienceCache::NAME], experience); + mExperiences->add(experience[LLExperienceCache::NAME], experience[LLExperienceCache::EXPERIENCE_ID].asUUID()); } void LLScriptEdCore::clearExperiences() { mExperiences->removeall(); - mExperiences->add("No Experience"); + mExperiences->add("No Experience", LLUUID::null); } LLUUID LLScriptEdCore::getSelectedExperience()const @@ -1281,6 +1308,19 @@ LLUUID LLScriptEdCore::getSelectedExperience()const return LLUUID::null; } +void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) +{ + mAssociatedExperience = experience_id; + if(experience_id.isNull()) + { + if(!mExperiences->setSelectedByValue(mAssociatedExperience, TRUE)) + { + mExperiences->setSelectedByValue(LLUUID::null, TRUE); + } + } +} + + @@ -1954,7 +1994,7 @@ void LLLiveLSLEditor::loadAsset() LLHost host(object->getRegion()->getIP(), object->getRegion()->getPort()); gMessageSystem->sendReliable(host); - */ + */ } } else @@ -1987,11 +2027,26 @@ void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, lldebugs << "LLLiveLSLEditor::onLoadComplete: got uuid " << asset_id << llendl; LLUUID* xored_id = (LLUUID*)user_data; - + + LLLiveLSLEditor* instance = LLFloaterReg::findTypedInstance("preview_scriptedit", *xored_id); if(instance ) { + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string lookup_url=region->getCapability("GetMetadata"); + //lookup_url = "http://127.0.0.1:12035/meta"; + if(!lookup_url.empty()) + { + lookup_url += "/"; + lookup_url += asset_id.asString(); + lookup_url += "/experience"; + LLHTTPClient::get(lookup_url, new ExperienceAssociationResponder(*xored_id)); + } + } + if( LL_ERR_NOERR == status ) { instance->loadScriptText(vfs, asset_id, type); @@ -2509,3 +2564,11 @@ BOOL LLLiveLSLEditor::monoChecked() const } return FALSE; } + +void LLLiveLSLEditor::setAssociatedExperience( const LLUUID& experience_id ) +{ + if(mScriptEd) + { + mScriptEd->setAssociatedExperience(experience_id); + } +} diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 27252d17a1..b29d16c58d 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -108,7 +108,8 @@ public: virtual bool hasAccelerators() const { return true; } void addExperienceInfo( const LLSD& experience ); void clearExperiences(); - LLUUID getSelectedExperience()const; + LLUUID getSelectedExperience()const; + void setAssociatedExperience( const LLUUID& experience_id ); private: void onBtnHelp(); @@ -139,7 +140,7 @@ private: void (*mLoadCallback)(void* userdata); void (*mSaveCallback)(void* userdata, BOOL close_after_save); void (*mSearchReplaceCallback) (void* userdata); - void* mUserdata; + void* mUserdata; LLComboBox *mFunctions; LLComboBox *mExperiences; BOOL mForceClose; @@ -153,6 +154,7 @@ private: BOOL mEnableSave; BOOL mHasScriptData; LLLiveLSLFile* mLiveFile; + LLUUID mAssociatedExperience; LLScriptEdContainer* mContainer; // parent view }; @@ -234,7 +236,8 @@ public: /*virtual*/ BOOL postBuild(); - void setIsNew() { mIsNew = TRUE; } + void setIsNew() { mIsNew = TRUE; } + void setAssociatedExperience( const LLUUID& experience_id ); private: virtual BOOL canClose(); @@ -285,7 +288,7 @@ private: S32 mPendingUploads; BOOL getIsModifiable() const { return mIsModifiable; } // Evaluated on load assert - + LLCheckBoxCtrl* mMonoCheckbox; BOOL mIsModifiable; }; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index cd33442f7c..421e354610 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1599,7 +1599,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("GetExperiences"); capabilityNames.append("GetExperienceInfo"); capabilityNames.append("GetCreatorExperiences"); - capabilityNames.append("GetMesh"); + capabilityNames.append("GetMesh"); + capabilityNames.append("GetMetadata"); capabilityNames.append("GetObjectCost"); capabilityNames.append("GetObjectPhysicsData"); capabilityNames.append("GetTexture"); -- cgit v1.2.3 From 10c2758a057b7287894acbaf909b21b7fc656c06 Mon Sep 17 00:00:00 2001 From: dolphin Date: Mon, 20 May 2013 22:07:41 -0700 Subject: fixed LLScriptEdCore::getSelectedExperience --- indra/newview/llpreviewscript.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 59a206b4d1..0d7a8678a1 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1300,12 +1300,7 @@ void LLScriptEdCore::clearExperiences() LLUUID LLScriptEdCore::getSelectedExperience()const { - LLSD value = mExperiences->getSelectedValue(); - if(value.has(LLExperienceCache::EXPERIENCE_ID)) - { - return value[LLExperienceCache::EXPERIENCE_ID].asUUID(); - } - return LLUUID::null; + return (LLUUID)mExperiences->getSelectedValue(); } void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) -- cgit v1.2.3 From 3d45290f8511e435f4a5bd03e485eb3b1758a332 Mon Sep 17 00:00:00 2001 From: dolphin Date: Mon, 20 May 2013 22:54:05 -0700 Subject: changing cast for the mac build --- indra/newview/llpreviewscript.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 0d7a8678a1..7c1b83dfa9 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1300,7 +1300,7 @@ void LLScriptEdCore::clearExperiences() LLUUID LLScriptEdCore::getSelectedExperience()const { - return (LLUUID)mExperiences->getSelectedValue(); + return mExperiences->getSelectedValue().asUUID(); } void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) -- cgit v1.2.3 From 37bd01152ac278e3401a96b12baf5505c5c1f789 Mon Sep 17 00:00:00 2001 From: dolphin Date: Tue, 21 May 2013 22:57:15 -0700 Subject: Update experience request to use the new post service. --- indra/newview/llpreviewscript.cpp | 40 +++++++++++++++++++++++++-------------- indra/newview/llpreviewscript.h | 1 + 2 files changed, 27 insertions(+), 14 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 7c1b83dfa9..bba0f1330c 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1290,6 +1290,10 @@ void LLScriptEdCore::addExperienceInfo( const LLSD& experience ) { mExperiences->setEnabled(TRUE); mExperiences->add(experience[LLExperienceCache::NAME], experience[LLExperienceCache::EXPERIENCE_ID].asUUID()); + if(mAssociatedExperience == experience[LLExperienceCache::EXPERIENCE_ID].asUUID()) + { + setAssociatedExperience(mAssociatedExperience); + } } void LLScriptEdCore::clearExperiences() @@ -1306,7 +1310,7 @@ LLUUID LLScriptEdCore::getSelectedExperience()const void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) { mAssociatedExperience = experience_id; - if(experience_id.isNull()) + if(experience_id.notNull()) { if(!mExperiences->setSelectedByValue(mAssociatedExperience, TRUE)) { @@ -2028,19 +2032,8 @@ void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, if(instance ) { - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - std::string lookup_url=region->getCapability("GetMetadata"); - //lookup_url = "http://127.0.0.1:12035/meta"; - if(!lookup_url.empty()) - { - lookup_url += "/"; - lookup_url += asset_id.asString(); - lookup_url += "/experience"; - LLHTTPClient::get(lookup_url, new ExperienceAssociationResponder(*xored_id)); - } - } + instance->fetchAssociatedExperience(asset_id); + if( LL_ERR_NOERR == status ) { @@ -2488,6 +2481,25 @@ void LLLiveLSLEditor::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* use delete data; } +void LLLiveLSLEditor::fetchAssociatedExperience(const LLUUID& asset_id) +{ + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string lookup_url=region->getCapability("GetMetadata"); + if(!lookup_url.empty()) + { + LLSD request; + request["asset-id"]=asset_id; + LLSD fields; + fields.append("experience"); + request["fields"] = fields; + LLHTTPClient::post(lookup_url, request, new ExperienceAssociationResponder(getKey())); + } + } +} + + BOOL LLLiveLSLEditor::canClose() { return (mScriptEd->canClose()); diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index b29d16c58d..28e71b111a 100755 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -238,6 +238,7 @@ public: void setIsNew() { mIsNew = TRUE; } void setAssociatedExperience( const LLUUID& experience_id ); + void fetchAssociatedExperience(const LLUUID& asset_id); private: virtual BOOL canClose(); -- cgit v1.2.3 From 08a1ae6839f07aeadb58326208c2c01b2ef667c3 Mon Sep 17 00:00:00 2001 From: dolphin Date: Tue, 6 Aug 2013 16:05:09 -0700 Subject: Checkpoint script editor --- indra/newview/llfloaterscriptexperience.cpp | 13 + indra/newview/llfloaterscriptexperience.h | 45 ++ indra/newview/llpreviewscript.cpp | 91 +++- indra/newview/llpreviewscript.h | 15 +- .../skins/default/xui/en/panel_script_ed.xml | 509 ++++++++++++--------- .../default/xui/en/panel_script_experience.xml | 97 ++++ 6 files changed, 544 insertions(+), 226 deletions(-) create mode 100644 indra/newview/llfloaterscriptexperience.cpp create mode 100644 indra/newview/llfloaterscriptexperience.h create mode 100644 indra/newview/skins/default/xui/en/panel_script_experience.xml (limited to 'indra') diff --git a/indra/newview/llfloaterscriptexperience.cpp b/indra/newview/llfloaterscriptexperience.cpp new file mode 100644 index 0000000000..497522e821 --- /dev/null +++ b/indra/newview/llfloaterscriptexperience.cpp @@ -0,0 +1,13 @@ +#include "llviewerprecompiledheaders.h" + +#include "llfloaterscriptexperience.h" + +LLFloaterScriptExperience::LLFloaterScriptExperience(const LLSD& data) + :LLFloater(data) +{ +} + +BOOL LLFloaterScriptExperience::postBuild() +{ + return TRUE; +} diff --git a/indra/newview/llfloaterscriptexperience.h b/indra/newview/llfloaterscriptexperience.h new file mode 100644 index 0000000000..c79ff78131 --- /dev/null +++ b/indra/newview/llfloaterscriptexperience.h @@ -0,0 +1,45 @@ +/** + * @file llfloaterscriptexperience.h + * @brief LLFloaterScriptExperience class definition + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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_LLFLOATERSCRIPTEXPERIENCE_H +#define LL_LLFLOATERSCRIPTEXPERIENCE_H + +#include "llfloater.h" + +class LLFloaterScriptExperience : + public LLFloater +{ +public: + LLFloaterScriptExperience(const LLSD& data); + +protected: + /*virtual*/ BOOL postBuild(); + +private: + +}; + +#endif //LL_LLFLOATERSCRIPTEXPERIENCE_H diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index bba0f1330c..ef2eb98ce9 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -378,9 +378,43 @@ LLScriptEdCore::~LLScriptEdCore() void LLScriptEdCore::experienceChanged() { enableSave(TRUE); - getChildView("Save_btn")->setEnabled(true); + getChildView("Save_btn")->setEnabled(TRUE); + mAssociatedExperience = mExperiences->getSelectedValue().asUUID(); } +void LLScriptEdCore::onToggleExperience( LLUICtrl *ui, void* userdata ) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + BOOL checked = self->mExperienceEnabled->get(); + BOOL expand = checked && self->mAssociatedExperience.isNull(); + self->mExpandExperience->setToggleState(expand); + self->mExpandExperience->setEnabled(checked); + self->mExperienceDetails->setVisible(expand); + if(!checked) + { + if(self->mAssociatedExperience.notNull()) + { + self->enableSave(TRUE); + } + self->setAssociatedExperience(LLUUID::null); + } + else if (expand) + { + self->mExperiences->selectFirstItem(); + self->setAssociatedExperience(self->mExperiences->getSelectedValue().asUUID()); + self->enableSave(TRUE); + } +} + +void LLScriptEdCore::onToggleExperienceDetails( void* userdata ) +{ + LLScriptEdCore* self = (LLScriptEdCore*)userdata; + BOOL checked = self->mExperienceEnabled->get(); + BOOL expanded = self->mExpandExperience->getToggleState(); + self->mExperienceDetails->setVisible(checked && expanded); +} + + BOOL LLScriptEdCore::postBuild() { mErrorList = getChild("lsl errors"); @@ -390,6 +424,14 @@ BOOL LLScriptEdCore::postBuild() mExperiences = getChild("Experiences..."); mExperiences->setCommitCallback(boost::bind(&LLScriptEdCore::experienceChanged, this)); + mExperienceEnabled = getChild("enable_xp"); + mExpandExperience = getChild("Expand Experience"); + mExperienceDetails = getChild("Experience Details"); + mExpandExperience->setClickedCallback(boost::bind(&LLScriptEdCore::onToggleExperienceDetails, this)); + + childSetCommitCallback("enable_xp", onToggleExperience, this); + + childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this); mEditor = getChild("Script Editor"); @@ -1258,10 +1300,15 @@ public: LLLiveLSLEditor* scriptCore = LLFloaterReg::findTypedInstance("preview_scriptedit", mParent); - if(!scriptCore || !content.has("experience")) + if(!scriptCore) return; - scriptCore->setAssociatedExperience(content["experience"].asUUID()); + LLUUID id; + if(content.has("experience")) + { + id=content["experience"].asUUID(); + } + scriptCore->setAssociatedExperience(id); } virtual void error(U32 status, const std::string& reason) @@ -1294,31 +1341,53 @@ void LLScriptEdCore::addExperienceInfo( const LLSD& experience ) { setAssociatedExperience(mAssociatedExperience); } + LLUICtrl* no_experiences=getChild("No Experiences"); + if(no_experiences) + { + no_experiences->setVisible(FALSE); + } } void LLScriptEdCore::clearExperiences() { mExperiences->removeall(); - mExperiences->add("No Experience", LLUUID::null); + mExperiences->setEnabled(FALSE); + LLUICtrl* no_experiences=getChild("No Experiences"); + if(no_experiences) + { + no_experiences->setVisible(TRUE); + } } -LLUUID LLScriptEdCore::getSelectedExperience()const +LLUUID LLScriptEdCore::getAssociatedExperience()const { - return mExperiences->getSelectedValue().asUUID(); + return mAssociatedExperience; } void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) { + mExperienceEnabled->setEnabled(TRUE); mAssociatedExperience = experience_id; if(experience_id.notNull()) { - if(!mExperiences->setSelectedByValue(mAssociatedExperience, TRUE)) - { - mExperiences->setSelectedByValue(LLUUID::null, TRUE); - } + LLExperienceCache::get(experience_id, boost::bind(&LLScriptEdCore::addAssociatedExperience, this, _1)); } } +void LLScriptEdCore::addAssociatedExperience(const LLSD& experience) +{ + + if(mExperiences->setSelectedByValue(mAssociatedExperience, TRUE)) + { + mExperienceEnabled->set(TRUE); + mExpandExperience->setEnabled(TRUE); + } + else + { + mExperiences->add(experience[LLExperienceCache::NAME], experience[LLExperienceCache::EXPERIENCE_ID].asUUID(), ADD_TOP, FALSE); + mExperienceEnabled->set(FALSE); + } +} @@ -2284,7 +2353,7 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) BOOL is_running = getChild( "running")->get(); if (!url.empty()) { - uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running, mScriptEd->getSelectedExperience()); + uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running, mScriptEd->getAssociatedExperience()); } else if (gAssetStorage) { diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 28e71b111a..043bf9c0b7 100755 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -100,7 +100,9 @@ public: static void onBtnInsertSample(void*); static void onBtnInsertFunction(LLUICtrl*, void*); static void onBtnLoadFromFile(void*); - static void onBtnSaveToFile(void*); + static void onBtnSaveToFile(void*); + static void onToggleExperience(LLUICtrl *ui, void* userdata); + static void onToggleExperienceDetails(void* userdata); static bool enableSaveToFileMenu(void* userdata); static bool enableLoadFromFileMenu(void* userdata); @@ -108,7 +110,8 @@ public: virtual bool hasAccelerators() const { return true; } void addExperienceInfo( const LLSD& experience ); void clearExperiences(); - LLUUID getSelectedExperience()const; + LLUUID getAssociatedExperience()const; + void setAssociatedExperience( const LLUUID& experience_id ); private: @@ -125,7 +128,8 @@ private: void enableSave(BOOL b) {mEnableSave = b;} void requestExperiences(); - void experienceChanged(); + void experienceChanged(); + void addAssociatedExperience(const LLSD& experience); protected: void deleteBridges(); @@ -142,7 +146,10 @@ private: void (*mSearchReplaceCallback) (void* userdata); void* mUserdata; LLComboBox *mFunctions; - LLComboBox *mExperiences; + LLComboBox *mExperiences; + LLCheckBoxCtrl *mExperienceEnabled; + LLUICtrl *mExperienceDetails; + LLButton *mExpandExperience; BOOL mForceClose; LLPanel* mCodePanel; LLScrollListCtrl* mErrorList; diff --git a/indra/newview/skins/default/xui/en/panel_script_ed.xml b/indra/newview/skins/default/xui/en/panel_script_ed.xml index 7e4ac1d7fb..331157d713 100755 --- a/indra/newview/skins/default/xui/en/panel_script_ed.xml +++ b/indra/newview/skins/default/xui/en/panel_script_ed.xml @@ -1,216 +1,303 @@ - - Loading... - - - You can not view or edit this script, since it has been set as "no copy". You need full permissions to view or edit a script inside an object. - - - Public Objects cannot run scripts - - - Running - - - Script: [NAME] - - - Select an editor by setting the environment variable LL_SCRIPT_EDITOR or the ExternalEditor setting. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Loading... - - + + Loading... + + + You can not view or edit this script, since it has been set as "no copy". You need full permissions to view or edit a script inside an object. + + + Public Objects cannot run scripts + + + Running + + + Script: [NAME] + + + Select an editor by setting the environment variable LL_SCRIPT_EDITOR or the ExternalEditor setting. + + - - - + + + + + + + + + + + Associate with: + + + + + + + + + You are not a contributor to any experiences. + + + + diff --git a/indra/newview/skins/default/xui/en/panel_script_experience.xml b/indra/newview/skins/default/xui/en/panel_script_experience.xml new file mode 100644 index 0000000000..e798638751 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_script_experience.xml @@ -0,0 +1,97 @@ + + + + + + + + + Script: + + + Associated with: + + + You can contribute: + + + Associate with: + + + + + + EasySit Animator 1.2.4 + + + Kyle's Superhero RPG + + + Yes + + + + + + You are not a contributor to any experiences. + + -- cgit v1.2.3 From 6354ad379d0850b4cc3969130fb1b30b0e2c0ee9 Mon Sep 17 00:00:00 2001 From: dolphin Date: Thu, 8 Aug 2013 09:26:09 -0700 Subject: Simplified the experience panel to only the required elements --- indra/newview/llpreviewscript.cpp | 259 ++++++++++----------- indra/newview/llpreviewscript.h | 10 +- .../default/xui/en/floater_live_lsleditor.xml | 2 +- .../skins/default/xui/en/panel_script_ed.xml | 257 ++++++++------------ 4 files changed, 233 insertions(+), 295 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index ef2eb98ce9..bd8f65e767 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -119,6 +119,57 @@ static bool have_script_upload_cap(LLUUID& object_id) return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty()); } +class ExperienceAssociationResponder : public LLHTTPClient::Responder +{ +public: + ExperienceAssociationResponder(const LLUUID& parent):mParent(parent) + { + } + + LLUUID mParent; + + virtual void result(const LLSD& content) + { + + LLLiveLSLEditor* scriptCore = LLFloaterReg::findTypedInstance("preview_scriptedit", mParent); + + if(!scriptCore) + return; + + LLUUID id; + if(content.has("experience")) + { + id=content["experience"].asUUID(); + } + scriptCore->setAssociatedExperience(id); + } + + virtual void error(U32 status, const std::string& reason) + { + llinfos << "Failed to look up associated script: " << status << ": " << reason << llendl; + } + +}; + +class ExperienceResponder : public LLHTTPClient::Responder +{ +public: + ExperienceResponder(const LLHandle& parent):mParent(parent) + { + } + + LLHandle mParent; + + virtual void result(const LLSD& content) + { + LLScriptEdCore* scriptCore = mParent.get(); + if(!scriptCore) + return; + + scriptCore->setExperienceIds(content["experience_ids"]); + } +}; + /// --------------------------------------------------------------------------- /// LLLiveLSLFile /// --------------------------------------------------------------------------- @@ -377,44 +428,34 @@ LLScriptEdCore::~LLScriptEdCore() void LLScriptEdCore::experienceChanged() { - enableSave(TRUE); - getChildView("Save_btn")->setEnabled(TRUE); - mAssociatedExperience = mExperiences->getSelectedValue().asUUID(); + if(mAssociatedExperience != mExperiences->getSelectedValue().asUUID()) + { + enableSave(TRUE); + getChildView("Save_btn")->setEnabled(TRUE); + mAssociatedExperience = mExperiences->getSelectedValue().asUUID(); + } } void LLScriptEdCore::onToggleExperience( LLUICtrl *ui, void* userdata ) { LLScriptEdCore* self = (LLScriptEdCore*)userdata; - BOOL checked = self->mExperienceEnabled->get(); - BOOL expand = checked && self->mAssociatedExperience.isNull(); - self->mExpandExperience->setToggleState(expand); - self->mExpandExperience->setEnabled(checked); - self->mExperienceDetails->setVisible(expand); - if(!checked) + + LLUUID id; + if(self->mExperienceEnabled->get()) { - if(self->mAssociatedExperience.notNull()) + if(self->mAssociatedExperience.isNull()) { - self->enableSave(TRUE); + id=self->mExperienceIds.beginArray()->asUUID(); } - self->setAssociatedExperience(LLUUID::null); } - else if (expand) + + if(id != self->mAssociatedExperience) { - self->mExperiences->selectFirstItem(); - self->setAssociatedExperience(self->mExperiences->getSelectedValue().asUUID()); self->enableSave(TRUE); } + self->setAssociatedExperience(id); } -void LLScriptEdCore::onToggleExperienceDetails( void* userdata ) -{ - LLScriptEdCore* self = (LLScriptEdCore*)userdata; - BOOL checked = self->mExperienceEnabled->get(); - BOOL expanded = self->mExpandExperience->getToggleState(); - self->mExperienceDetails->setVisible(checked && expanded); -} - - BOOL LLScriptEdCore::postBuild() { mErrorList = getChild("lsl errors"); @@ -425,9 +466,6 @@ BOOL LLScriptEdCore::postBuild() mExperiences->setCommitCallback(boost::bind(&LLScriptEdCore::experienceChanged, this)); mExperienceEnabled = getChild("enable_xp"); - mExpandExperience = getChild("Expand Experience"); - mExperienceDetails = getChild("Experience Details"); - mExpandExperience->setClickedCallback(boost::bind(&LLScriptEdCore::onToggleExperienceDetails, this)); childSetCommitCallback("enable_xp", onToggleExperience, this); @@ -1247,152 +1285,107 @@ bool LLScriptEdCore::enableLoadFromFileMenu(void* userdata) return (self && self->mEditor) ? self->mEditor->canLoadOrSaveToFile() : FALSE; } - -void AddExperienceResult(LLHandle panel, const LLSD& experience) +LLUUID LLScriptEdCore::getAssociatedExperience()const { - LLScriptEdCore* scriptCore = panel.get(); - if(scriptCore) - { - scriptCore->addExperienceInfo(experience); - } + return mAssociatedExperience; } - -class ExperienceResponder : public LLHTTPClient::Responder +void LLScriptEdCore::setExperienceIds( const LLSD& experience_ids ) { -public: - ExperienceResponder(const LLHandle& parent):mParent(parent) - { - } - - LLHandle mParent; - - virtual void result(const LLSD& content) - { - LLScriptEdCore* scriptCore = mParent.get(); - if(!scriptCore) - return; - - scriptCore->clearExperiences(); + mExperienceIds=experience_ids; + updateExperiencePanel(); +} - 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, boost::bind(AddExperienceResult, mParent, _1)); - } - } -}; - -class ExperienceAssociationResponder : public LLHTTPClient::Responder +void LLScriptEdCore::updateExperiencePanel() { -public: - ExperienceAssociationResponder(const LLUUID& parent):mParent(parent) - { - } + BOOL editable = mEditor->getEnabled(); - LLUUID mParent; - - virtual void result(const LLSD& content) + if(mAssociatedExperience.isNull()) { - - LLLiveLSLEditor* scriptCore = LLFloaterReg::findTypedInstance("preview_scriptedit", mParent); - - if(!scriptCore) - return; - - LLUUID id; - if(content.has("experience")) + mExperienceEnabled->set(FALSE); + mExperiences->setVisible(FALSE); + if(mExperienceIds.size()>0) { - id=content["experience"].asUUID(); + mExperienceEnabled->setEnabled(TRUE); + mExperienceEnabled->setToolTip(getString("add_experiences")); } - scriptCore->setAssociatedExperience(id); + else + { + mExperienceEnabled->setEnabled(FALSE); + mExperienceEnabled->setToolTip(getString("no_experiences")); + } + getChild("view_profile")->setVisible(FALSE); } - - virtual void error(U32 status, const std::string& reason) + else { - llinfos << "Failed to look up associated script: " << status << ": " << reason << llendl; + mExperienceEnabled->setToolTip(getString("experience_enabled")); + mExperienceEnabled->setEnabled(editable); + mExperienceEnabled->set(TRUE); + mExperiences->setVisible(TRUE); + getChild("view_profile")->setVisible(TRUE); + buildExperienceList(); } - -}; - -void LLScriptEdCore::requestExperiences() -{ - mExperiences->setEnabled(FALSE); - - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - std::string lookup_url=region->getCapability("GetCreatorExperiences"); - if(!lookup_url.empty()) - { - LLHTTPClient::get(lookup_url, new ExperienceResponder(getDerivedHandle())); - } - } } -void LLScriptEdCore::addExperienceInfo( const LLSD& experience ) -{ - mExperiences->setEnabled(TRUE); - mExperiences->add(experience[LLExperienceCache::NAME], experience[LLExperienceCache::EXPERIENCE_ID].asUUID()); - if(mAssociatedExperience == experience[LLExperienceCache::EXPERIENCE_ID].asUUID()) +void LLScriptEdCore::addExperienceInfo(const LLSD& experience, BOOL enabled) +{ + LLUUID id = experience[LLExperienceCache::EXPERIENCE_ID].asUUID(); + EAddPosition position = (id == mAssociatedExperience)?ADD_TOP:ADD_BOTTOM; + LLScrollListItem* item=mExperiences->add(experience[LLExperienceCache::NAME], id, position ); + if(!enabled) { - setAssociatedExperience(mAssociatedExperience); - } - LLUICtrl* no_experiences=getChild("No Experiences"); - if(no_experiences) - { - no_experiences->setVisible(FALSE); + item->setEnabled(FALSE); } } -void LLScriptEdCore::clearExperiences() +void LLScriptEdCore::buildExperienceList() { - mExperiences->removeall(); - mExperiences->setEnabled(FALSE); - LLUICtrl* no_experiences=getChild("No Experiences"); - if(no_experiences) + mExperiences->clear(); + bool foundAssociated=false; + for(LLSD::array_const_iterator it = mExperienceIds.beginArray(); it != mExperienceIds.endArray(); ++it) { - no_experiences->setVisible(TRUE); + LLUUID id = it->asUUID(); + foundAssociated |= (id == mAssociatedExperience); + LLExperienceCache::get(id, boost::bind(&LLScriptEdCore::addExperienceInfo, this, _1, TRUE)); + } + + if(!foundAssociated ) + { + LLExperienceCache::get(mAssociatedExperience, boost::bind(&LLScriptEdCore::addExperienceInfo, this, _1, FALSE)); } -} -LLUUID LLScriptEdCore::getAssociatedExperience()const -{ - return mAssociatedExperience; } + void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) { - mExperienceEnabled->setEnabled(TRUE); mAssociatedExperience = experience_id; - if(experience_id.notNull()) - { - LLExperienceCache::get(experience_id, boost::bind(&LLScriptEdCore::addAssociatedExperience, this, _1)); - } + updateExperiencePanel(); } -void LLScriptEdCore::addAssociatedExperience(const LLSD& experience) -{ - if(mExperiences->setSelectedByValue(mAssociatedExperience, TRUE)) + +void LLScriptEdCore::requestExperiences() +{ + if (!mEditor->getEnabled()) { - mExperienceEnabled->set(TRUE); - mExpandExperience->setEnabled(TRUE); + return; } - else + + LLViewerRegion* region = gAgent.getRegion(); + if (region) { - mExperiences->add(experience[LLExperienceCache::NAME], experience[LLExperienceCache::EXPERIENCE_ID].asUUID(), ADD_TOP, FALSE); - mExperienceEnabled->set(FALSE); + std::string lookup_url=region->getCapability("GetCreatorExperiences"); + if(!lookup_url.empty()) + { + LLHTTPClient::get(lookup_url, new ExperienceResponder(getDerivedHandle())); + } } } - - /// --------------------------------------------------------------------------- /// LLScriptEdContainer /// --------------------------------------------------------------------------- diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 043bf9c0b7..9578396f91 100755 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -102,17 +102,18 @@ public: static void onBtnLoadFromFile(void*); static void onBtnSaveToFile(void*); static void onToggleExperience(LLUICtrl *ui, void* userdata); - static void onToggleExperienceDetails(void* userdata); static bool enableSaveToFileMenu(void* userdata); static bool enableLoadFromFileMenu(void* userdata); virtual bool hasAccelerators() const { return true; } - void addExperienceInfo( const LLSD& experience ); - void clearExperiences(); + void addExperienceInfo( const LLSD& experience, BOOL enabled ); + void setExperienceIds(const LLSD& experience_ids); + void buildExperienceList(); LLUUID getAssociatedExperience()const; void setAssociatedExperience( const LLUUID& experience_id ); + void updateExperiencePanel(); private: void onBtnHelp(); @@ -148,8 +149,6 @@ private: LLComboBox *mFunctions; LLComboBox *mExperiences; LLCheckBoxCtrl *mExperienceEnabled; - LLUICtrl *mExperienceDetails; - LLButton *mExpandExperience; BOOL mForceClose; LLPanel* mCodePanel; LLScrollListCtrl* mErrorList; @@ -162,6 +161,7 @@ private: BOOL mHasScriptData; LLLiveLSLFile* mLiveFile; LLUUID mAssociatedExperience; + LLSD mExperienceIds; LLScriptEdContainer* mContainer; // parent view }; diff --git a/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml b/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml index 5cd7cd196d..562936ed80 100755 --- a/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml +++ b/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml @@ -7,7 +7,7 @@ height="580" layout="topleft" min_height="271" - min_width="290" + min_width="328" name="script ed float" help_topic="script_ed_float" save_rect="true" diff --git a/indra/newview/skins/default/xui/en/panel_script_ed.xml b/indra/newview/skins/default/xui/en/panel_script_ed.xml index 331157d713..18a8ddc1bf 100755 --- a/indra/newview/skins/default/xui/en/panel_script_ed.xml +++ b/indra/newview/skins/default/xui/en/panel_script_ed.xml @@ -3,7 +3,7 @@ bevel_style="none" border_style="line" follows="left|top|right|bottom" - height="730" + height="515" layout="topleft" left="0" name="script panel" @@ -32,6 +32,18 @@ name="external_editor_not_set"> Select an editor by setting the environment variable LL_SCRIPT_EDITOR or the ExternalEditor setting. + + Uncheck to remove the current experience + + + You are not authorized for any experiences + + + Select to add an experience + - - - - Loading... - - - - - - - - - - - - - - - - - - Associate with: - - - - - - - - - You are not a contributor to any experiences. - + + Loading... + + + + +