From d6f1dc06ee590aa2aeb9d3eb953a1217a83c9482 Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Fri, 13 Apr 2018 18:29:47 +0300 Subject: [PATCH 01/20] MAINT-8532 FIXED Group invite text is overlapped when an empty string is present in the group description ending --- indra/newview/llnotificationlistitem.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/indra/newview/llnotificationlistitem.cpp b/indra/newview/llnotificationlistitem.cpp index cbcf9cac9c..f2de8e54a0 100644 --- a/indra/newview/llnotificationlistitem.cpp +++ b/indra/newview/llnotificationlistitem.cpp @@ -288,9 +288,7 @@ BOOL LLGroupInviteNotificationListItem::postBuild() //invitation with any non-default group role, doesn't have newline characters at the end unlike simple invitations std::string invitation_desc = mNoticeTextExp->getValue().asString(); - boost::regex pattern = boost::regex("\n\n$", boost::regex::perl|boost::regex::icase); - boost::match_results matches; - if(!boost::regex_search(invitation_desc, matches, pattern)) + if (invitation_desc.substr(invitation_desc.size() - 2) != "\n\n") { invitation_desc += "\n\n"; mNoticeTextExp->setValue(invitation_desc); From 1bdf76dde7aa7cd9c2d0fbb589ac44f6b3788d33 Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Mon, 16 Apr 2018 16:27:59 +0300 Subject: [PATCH 02/20] MAINT-8453 FIXED The "Reload page" button is displayed for some time after opening media browser --- indra/newview/llfloaterwebcontent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp index 8a8d92d459..3b17368445 100644 --- a/indra/newview/llfloaterwebcontent.cpp +++ b/indra/newview/llfloaterwebcontent.cpp @@ -105,6 +105,7 @@ BOOL LLFloaterWebContent::postBuild() // these buttons are always enabled mBtnReload->setEnabled( true ); + mBtnReload->setVisible( false ); getChildView("popexternal")->setEnabled( true ); // cache image for secure browsing From 6bb9ec41950d8f83c8659e36850aa47e8ea601b8 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 17 Apr 2018 13:11:53 +0000 Subject: [PATCH 03/20] MAINT-8498 Fixed item offers not appearing after friednship offer --- indra/newview/llchathistory.cpp | 71 +++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index a9e8e77a0b..97a71a8802 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -53,6 +53,7 @@ #include "llstylemap.h" #include "llslurl.h" #include "lllayoutstack.h" +#include "llnotifications.h" #include "llnotificationsutil.h" #include "lltoastnotifypanel.h" #include "lltooltip.h" @@ -1315,44 +1316,52 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL // notify processing if (chat.mNotifId.notNull()) { - bool create_toast = true; - for (LLToastNotifyPanel::instance_iter ti(LLToastNotifyPanel::beginInstances()) - , tend(LLToastNotifyPanel::endInstances()); ti != tend; ++ti) - { - LLToastNotifyPanel& panel = *ti; - LLIMToastNotifyPanel * imtoastp = dynamic_cast(&panel); - const std::string& notification_name = panel.getNotificationName(); - if (notification_name == "OfferFriendship" && panel.isControlPanelEnabled() && imtoastp) - { - create_toast = false; - break; - } - } - - if (create_toast) - { LLNotificationPtr notification = LLNotificationsUtil::find(chat.mNotifId); if (notification != NULL) { - LLIMToastNotifyPanel* notify_box = new LLIMToastNotifyPanel( - notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history, mEditor); + bool create_toast = true; + if (notification->getName() == "OfferFriendship") + { + // We don't want multiple friendship offers to appear, this code checks if there are previous offers + // by iterating though all panels. + // Note: it might be better to simply add a "pending offer" flag somewhere + for (LLToastNotifyPanel::instance_iter ti(LLToastNotifyPanel::beginInstances()) + , tend(LLToastNotifyPanel::endInstances()); ti != tend; ++ti) + { + LLToastNotifyPanel& panel = *ti; + LLIMToastNotifyPanel * imtoastp = dynamic_cast(&panel); + const std::string& notification_name = panel.getNotificationName(); + if (notification_name == "OfferFriendship" + && panel.isControlPanelEnabled() + && imtoastp) + { + create_toast = false; + break; + } + } + } - //Prepare the rect for the view - LLRect target_rect = mEditor->getDocumentView()->getRect(); - // squeeze down the widget by subtracting padding off left and right - target_rect.mLeft += mLeftWidgetPad + mEditor->getHPad(); - target_rect.mRight -= mRightWidgetPad; - notify_box->reshape(target_rect.getWidth(), notify_box->getRect().getHeight()); - notify_box->setOrigin(target_rect.mLeft, notify_box->getRect().mBottom); + if (create_toast) + { + LLIMToastNotifyPanel* notify_box = new LLIMToastNotifyPanel( + notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history, mEditor); - LLInlineViewSegment::Params params; - params.view = notify_box; - params.left_pad = mLeftWidgetPad; - params.right_pad = mRightWidgetPad; - mEditor->appendWidget(params, "\n", false); + //Prepare the rect for the view + LLRect target_rect = mEditor->getDocumentView()->getRect(); + // squeeze down the widget by subtracting padding off left and right + target_rect.mLeft += mLeftWidgetPad + mEditor->getHPad(); + target_rect.mRight -= mRightWidgetPad; + notify_box->reshape(target_rect.getWidth(), notify_box->getRect().getHeight()); + notify_box->setOrigin(target_rect.mLeft, notify_box->getRect().mBottom); + + LLInlineViewSegment::Params params; + params.view = notify_box; + params.left_pad = mLeftWidgetPad; + params.right_pad = mRightWidgetPad; + mEditor->appendWidget(params, "\n", false); + } } } - } // usual messages showing else From 1fc32d4c973275cf86d2b936e7bb3241e0155197 Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Tue, 17 Apr 2018 18:39:31 +0300 Subject: [PATCH 04/20] MAINT-8458 FIXED Fields for new group notice dissapears immediately after appearing --- indra/newview/llpanelgroupnotices.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index 48b659a81e..178b5db6c2 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -575,6 +575,7 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg) mNoticesList->setNeedsSort(save_sort); mNoticesList->updateSort(); + mNoticesList->selectFirstItem(); } void LLPanelGroupNotices::onSelectNotice(LLUICtrl* ctrl, void* data) From 4d3642b2fab163668b1d67c0a6b631417e71e02d Mon Sep 17 00:00:00 2001 From: andreykproductengine Date: Thu, 19 Apr 2018 18:37:35 +0300 Subject: [PATCH 05/20] MAINT-7626 Incorporate transfer changes into viewer --- indra/llmessage/llxfer.cpp | 43 ++- indra/llmessage/llxfer.h | 6 +- indra/llmessage/llxfer_file.cpp | 40 ++- indra/llmessage/llxfer_file.h | 6 +- indra/llmessage/llxfer_mem.cpp | 22 -- indra/llmessage/llxfer_mem.h | 1 - indra/llmessage/llxfer_vfile.cpp | 32 +++ indra/llmessage/llxfer_vfile.h | 6 +- indra/llmessage/llxfermanager.cpp | 425 +++++++++++++++++------------- indra/llmessage/llxfermanager.h | 21 +- 10 files changed, 364 insertions(+), 238 deletions(-) diff --git a/indra/llmessage/llxfer.cpp b/indra/llmessage/llxfer.cpp index e0590dfdff..a42ecefd23 100644 --- a/indra/llmessage/llxfer.cpp +++ b/indra/llmessage/llxfer.cpp @@ -99,7 +99,22 @@ void LLXfer::cleanup () S32 LLXfer::startSend (U64 xfer_id, const LLHost &remote_host) { - LL_WARNS() << "undifferentiated LLXfer::startSend for " << getFileName() << LL_ENDL; + LL_WARNS("Xfer") << "unexpected call to base class LLXfer::startSend for " << getFileName() << LL_ENDL; + return (-1); +} + +/////////////////////////////////////////////////////////// + +void LLXfer::closeFileHandle() +{ + LL_WARNS("Xfer") << "unexpected call to base class LLXfer::closeFileHandle for " << getFileName() << LL_ENDL; +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer::reopenFileHandle() +{ + LL_WARNS("Xfer") << "unexpected call to base class LLXfer::reopenFileHandle for " << getFileName() << LL_ENDL; return (-1); } @@ -127,14 +142,14 @@ S32 LLXfer::receiveData (char *datap, S32 data_size) S32 retval = 0; if (((S32) mBufferLength + data_size) > getMaxBufferSize()) - { + { // Write existing data to disk if it's larger than the buffer size retval = flush(); } if (!retval) { if (datap != NULL) - { + { // Append new data to mBuffer memcpy(&mBuffer[mBufferLength],datap,data_size); /*Flawfinder: ignore*/ mBufferLength += data_size; } @@ -248,7 +263,12 @@ void LLXfer::sendPacket(S32 packet_num) gMessageSystem->nextBlockFast(_PREHASH_DataPacket); gMessageSystem->addBinaryDataFast(_PREHASH_Data, &fdata_buf,fdata_size); - gMessageSystem->sendMessage(mRemoteHost); + S32 sent_something = gMessageSystem->sendMessage(mRemoteHost); + if (sent_something == 0) + { + abort(LL_ERR_CIRCUIT_GONE); + return; + } ACKTimer.reset(); mWaitingForACK = TRUE; @@ -326,12 +346,15 @@ void LLXfer::abort (S32 result_code) LL_INFOS() << "Aborting xfer from " << mRemoteHost << " named " << getFileName() << " - error: " << result_code << LL_ENDL; - gMessageSystem->newMessageFast(_PREHASH_AbortXfer); - gMessageSystem->nextBlockFast(_PREHASH_XferID); - gMessageSystem->addU64Fast(_PREHASH_ID, mID); - gMessageSystem->addS32Fast(_PREHASH_Result, result_code); - - gMessageSystem->sendMessage(mRemoteHost); + if (result_code != LL_ERR_CIRCUIT_GONE) + { + gMessageSystem->newMessageFast(_PREHASH_AbortXfer); + gMessageSystem->nextBlockFast(_PREHASH_XferID); + gMessageSystem->addU64Fast(_PREHASH_ID, mID); + gMessageSystem->addS32Fast(_PREHASH_Result, result_code); + + gMessageSystem->sendMessage(mRemoteHost); + } mStatus = e_LL_XFER_ABORTED; } diff --git a/indra/llmessage/llxfer.h b/indra/llmessage/llxfer.h index edf5eeb82d..3be8da4fbe 100644 --- a/indra/llmessage/llxfer.h +++ b/indra/llmessage/llxfer.h @@ -62,7 +62,7 @@ class LLXfer S32 mXferSize; char *mBuffer; - U32 mBufferLength; + U32 mBufferLength; // Size of valid data, not actual allocated buffer size U32 mBufferStartOffset; BOOL mBufferContainsEOF; @@ -90,7 +90,9 @@ class LLXfer void init(S32 chunk_size); virtual void cleanup(); - virtual S32 startSend (U64 xfer_id, const LLHost &remote_host); + virtual S32 startSend(U64 xfer_id, const LLHost &remote_host); + virtual void closeFileHandle(); + virtual S32 reopenFileHandle(); virtual void sendPacket(S32 packet_num); virtual void sendNextPacket(); virtual void resendLastPacket(); diff --git a/indra/llmessage/llxfer_file.cpp b/indra/llmessage/llxfer_file.cpp index 8e2ed890e7..ad55e389f3 100644 --- a/indra/llmessage/llxfer_file.cpp +++ b/indra/llmessage/llxfer_file.cpp @@ -206,7 +206,8 @@ S32 LLXfer_File::startSend (U64 xfer_id, const LLHost &remote_host) mBufferLength = 0; mBufferStartOffset = 0; - + + // We leave the file open, assuming we'll start reading and sending soon mFp = LLFile::fopen(mLocalFilename,"rb"); /* Flawfinder : ignore */ if (mFp) { @@ -232,6 +233,36 @@ S32 LLXfer_File::startSend (U64 xfer_id, const LLHost &remote_host) return (retval); } +/////////////////////////////////////////////////////////// +void LLXfer_File::closeFileHandle() +{ + if (mFp) + { + fclose(mFp); + mFp = NULL; + } +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer_File::reopenFileHandle() +{ + S32 retval = LL_ERR_NOERR; // presume success + + if (mFp == NULL) + { + mFp = LLFile::fopen(mLocalFilename,"rb"); /* Flawfinder : ignore */ + if (mFp == NULL) + { + LL_INFOS("Xfer") << "Warning: " << mLocalFilename << " not found when re-opening file" << LL_ENDL; + retval = LL_ERR_FILE_NOT_FOUND; + } + } + + return retval; +} + + /////////////////////////////////////////////////////////// S32 LLXfer_File::getMaxBufferSize () @@ -285,9 +316,12 @@ S32 LLXfer_File::flush() if (mFp) { - if (fwrite(mBuffer,1,mBufferLength,mFp) != mBufferLength) + S32 write_size = fwrite(mBuffer,1,mBufferLength,mFp); + if (write_size != mBufferLength) { - LL_WARNS() << "Short write" << LL_ENDL; + LL_WARNS("Xfer") << "Non-matching write size, requested " << mBufferLength + << " but wrote " << write_size + << LL_ENDL; } // LL_INFOS() << "******* wrote " << mBufferLength << " bytes of file xfer" << LL_ENDL; diff --git a/indra/llmessage/llxfer_file.h b/indra/llmessage/llxfer_file.h index a37dda6732..ab9374549e 100644 --- a/indra/llmessage/llxfer_file.h +++ b/indra/llmessage/llxfer_file.h @@ -61,8 +61,10 @@ class LLXfer_File : public LLXfer virtual S32 startDownload(); virtual S32 processEOF(); - - virtual S32 startSend (U64 xfer_id, const LLHost &remote_host); + + virtual S32 startSend(U64 xfer_id, const LLHost &remote_host); + virtual void closeFileHandle(); + virtual S32 reopenFileHandle(); virtual S32 suck(S32 start_position); virtual S32 flush(); diff --git a/indra/llmessage/llxfer_mem.cpp b/indra/llmessage/llxfer_mem.cpp index 3bea08f2e5..78a3e4f558 100644 --- a/indra/llmessage/llxfer_mem.cpp +++ b/indra/llmessage/llxfer_mem.cpp @@ -80,28 +80,6 @@ void LLXfer_Mem::setXferSize (S32 xfer_size) /////////////////////////////////////////////////////////// -U64 LLXfer_Mem::registerXfer(U64 xfer_id, const void *datap, const S32 length) -{ - mID = xfer_id; - - if (datap) - { - setXferSize(length); - if (mBuffer) - { - memcpy(mBuffer,datap,length); /* Flawfinder : ignore */ - mBufferLength = length; - } - else - { - xfer_id = 0; - } - } - - mStatus = e_LL_XFER_REGISTERED; - return (xfer_id); -} - S32 LLXfer_Mem::startSend (U64 xfer_id, const LLHost &remote_host) { S32 retval = LL_ERR_NOERR; // presume success diff --git a/indra/llmessage/llxfer_mem.h b/indra/llmessage/llxfer_mem.h index b5adf837df..d07779de87 100644 --- a/indra/llmessage/llxfer_mem.h +++ b/indra/llmessage/llxfer_mem.h @@ -53,7 +53,6 @@ class LLXfer_Mem : public LLXfer virtual void cleanup(); virtual S32 startSend (U64 xfer_id, const LLHost &remote_host); - virtual U64 registerXfer(U64 xfer_id, const void *datap, const S32 length); virtual void setXferSize (S32 data_size); virtual S32 initializeRequest(U64 xfer_id, diff --git a/indra/llmessage/llxfer_vfile.cpp b/indra/llmessage/llxfer_vfile.cpp index 4a378d1d34..5d41180192 100644 --- a/indra/llmessage/llxfer_vfile.cpp +++ b/indra/llmessage/llxfer_vfile.cpp @@ -205,6 +205,38 @@ S32 LLXfer_VFile::startSend (U64 xfer_id, const LLHost &remote_host) } /////////////////////////////////////////////////////////// + +void LLXfer_VFile::closeFileHandle() +{ + if (mVFile) + { + delete mVFile; + mVFile = NULL; + } +} + +/////////////////////////////////////////////////////////// + +S32 LLXfer_VFile::reopenFileHandle() +{ + S32 retval = LL_ERR_NOERR; // presume success + + if (mVFile == NULL) + { + mVFile =new LLVFile(mVFS, mLocalID, mType, LLVFile::READ); + if (mVFile == NULL) + { + LL_WARNS("Xfer") << "LLXfer_VFile::reopenFileHandle() can't read VFS file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; + retval = LL_ERR_FILE_NOT_FOUND; + } + } + + return retval; +} + + +/////////////////////////////////////////////////////////// + void LLXfer_VFile::setXferSize (S32 xfer_size) { LLXfer::setXferSize(xfer_size); diff --git a/indra/llmessage/llxfer_vfile.h b/indra/llmessage/llxfer_vfile.h index 048bf49dcc..b606c08122 100644 --- a/indra/llmessage/llxfer_vfile.h +++ b/indra/llmessage/llxfer_vfile.h @@ -66,8 +66,10 @@ class LLXfer_VFile : public LLXfer virtual S32 startDownload(); virtual S32 processEOF(); - - virtual S32 startSend (U64 xfer_id, const LLHost &remote_host); + + virtual S32 startSend(U64 xfer_id, const LLHost &remote_host); + virtual void closeFileHandle(); + virtual S32 reopenFileHandle(); virtual S32 suck(S32 start_position); virtual S32 flush(); diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp index 2ceb64ce8f..8f18004791 100644 --- a/indra/llmessage/llxfermanager.cpp +++ b/indra/llmessage/llxfermanager.cpp @@ -44,6 +44,8 @@ const S32 LL_PACKET_RETRY_LIMIT = 10; // packet retransmission limit const S32 LL_DEFAULT_MAX_SIMULTANEOUS_XFERS = 10; const S32 LL_DEFAULT_MAX_REQUEST_FIFO_XFERS = 1000; +const S32 LL_DEFAULT_MAX_HARD_LIMIT_SIMULTANEOUS_XFERS = 500; + #define LL_XFER_PROGRESS_MESSAGES 0 #define LL_XFER_TEST_REXMIT 0 @@ -66,10 +68,10 @@ LLXferManager::~LLXferManager () void LLXferManager::init (LLVFS *vfs) { - mSendList = NULL; - mReceiveList = NULL; + cleanup(); setMaxOutgoingXfersPerCircuit(LL_DEFAULT_MAX_SIMULTANEOUS_XFERS); + setHardLimitOutgoingXfersPerCircuit(LL_DEFAULT_MAX_HARD_LIMIT_SIMULTANEOUS_XFERS); setMaxIncomingXfers(LL_DEFAULT_MAX_REQUEST_FIFO_XFERS); mVFS = vfs; @@ -83,29 +85,14 @@ void LLXferManager::init (LLVFS *vfs) void LLXferManager::cleanup () { - LLXfer *xferp; - LLXfer *delp; - for_each(mOutgoingHosts.begin(), mOutgoingHosts.end(), DeletePointer()); mOutgoingHosts.clear(); - delp = mSendList; - while (delp) - { - xferp = delp->mNext; - delete delp; - delp = xferp; - } - mSendList = NULL; + for_each(mSendList.begin(), mSendList.end(), DeletePointer()); + mSendList.clear(); - delp = mReceiveList; - while (delp) - { - xferp = delp->mNext; - delete delp; - delp = xferp; - } - mReceiveList = NULL; + for_each(mReceiveList.begin(), mReceiveList.end(), DeletePointer()); + mReceiveList.clear(); } /////////////////////////////////////////////////////////// @@ -122,6 +109,11 @@ void LLXferManager::setMaxOutgoingXfersPerCircuit(S32 max_num) mMaxOutgoingXfersPerCircuit = max_num; } +void LLXferManager::setHardLimitOutgoingXfersPerCircuit(S32 max_num) +{ + mHardLimitOutgoingXfersPerCircuit = max_num; +} + void LLXferManager::setUseAckThrottling(const BOOL use) { mUseAckThrottling = use; @@ -148,19 +140,18 @@ void LLXferManager::setAckThrottleBPS(const F32 bps) void LLXferManager::updateHostStatus() { - LLXfer *xferp; - LLHostStatus *host_statusp = NULL; - for_each(mOutgoingHosts.begin(), mOutgoingHosts.end(), DeletePointer()); mOutgoingHosts.clear(); - for (xferp = mSendList; xferp; xferp = xferp->mNext) + for (xfer_list_t::iterator send_iter = mSendList.begin(); + send_iter != mSendList.end(); ++send_iter) { + LLHostStatus *host_statusp = NULL; for (status_list_t::iterator iter = mOutgoingHosts.begin(); iter != mOutgoingHosts.end(); ++iter) { host_statusp = *iter; - if (host_statusp->mHost == xferp->mRemoteHost) + if (host_statusp->mHost == (*send_iter)->mRemoteHost) { break; } @@ -170,23 +161,22 @@ void LLXferManager::updateHostStatus() host_statusp = new LLHostStatus(); if (host_statusp) { - host_statusp->mHost = xferp->mRemoteHost; + host_statusp->mHost = (*send_iter)->mRemoteHost; mOutgoingHosts.push_front(host_statusp); } } if (host_statusp) { - if (xferp->mStatus == e_LL_XFER_PENDING) + if ((*send_iter)->mStatus == e_LL_XFER_PENDING) { host_statusp->mNumPending++; } - else if (xferp->mStatus == e_LL_XFER_IN_PROGRESS) + else if ((*send_iter)->mStatus == e_LL_XFER_IN_PROGRESS) { host_statusp->mNumActive++; } } - - } + } } /////////////////////////////////////////////////////////// @@ -209,14 +199,15 @@ void LLXferManager::printHostStatus() /////////////////////////////////////////////////////////// -LLXfer *LLXferManager::findXfer (U64 id, LLXfer *list_head) +LLXfer *LLXferManager::findXfer(U64 id, xfer_list_t & xfer_list) { - LLXfer *xferp; - for (xferp = list_head; xferp; xferp = xferp->mNext) + for (xfer_list_t::iterator iter = xfer_list.begin(); + iter != xfer_list.end(); + ++iter) { - if (xferp->mID == id) + if ((*iter)->mID == id) { - return(xferp); + return(*iter); } } return(NULL); @@ -225,29 +216,34 @@ LLXfer *LLXferManager::findXfer (U64 id, LLXfer *list_head) /////////////////////////////////////////////////////////// -void LLXferManager::removeXfer (LLXfer *delp, LLXfer **list_head) +void LLXferManager::removeXfer(LLXfer *delp, xfer_list_t & xfer_list) { - // This function assumes that delp will only occur in the list - // zero or one times. if (delp) - { - if (*list_head == delp) + { + std::string direction = "send"; + if (&xfer_list == &mReceiveList) { - *list_head = delp->mNext; - delete (delp); + std::string direction = "receive"; + xfer_list = mSendList; } - else + + // This assumes that delp will occur in the list once at most + // Find the pointer in the list + for (xfer_list_t::iterator iter = xfer_list.begin(); + iter != xfer_list.end(); + ++iter) { - LLXfer *xferp = *list_head; - while (xferp->mNext) + if ((*iter) == delp) { - if (xferp->mNext == delp) - { - xferp->mNext = delp->mNext; - delete (delp); - break; - } - xferp = xferp->mNext; + LL_DEBUGS("Xfer") << "Deleting xfer to host " << (*iter)->mRemoteHost + << " of " << (*iter)->mXferSize << " bytes" + << ", status " << (S32)((*iter)->mStatus) + << " from the " << direction << " list" + << LL_ENDL; + + xfer_list.erase(iter); + delete (delp); + break; } } } @@ -272,7 +268,7 @@ U32 LLXferManager::numActiveListEntries(LLXfer *list_head) /////////////////////////////////////////////////////////// -S32 LLXferManager::numPendingXfers(const LLHost &host) +LLHostStatus * LLXferManager::findHostStatus(const LLHost &host) { LLHostStatus *host_statusp = NULL; @@ -282,26 +278,32 @@ S32 LLXferManager::numPendingXfers(const LLHost &host) host_statusp = *iter; if (host_statusp->mHost == host) { - return (host_statusp->mNumPending); + return (host_statusp); } } return 0; } +/////////////////////////////////////////////////////////// + +S32 LLXferManager::numPendingXfers(const LLHost &host) +{ + LLHostStatus *host_statusp = findHostStatus(host); + if (host_statusp) + { + return host_statusp->mNumPending; + } + return 0; +} + /////////////////////////////////////////////////////////// S32 LLXferManager::numActiveXfers(const LLHost &host) { - LLHostStatus *host_statusp = NULL; - - for (status_list_t::iterator iter = mOutgoingHosts.begin(); - iter != mOutgoingHosts.end(); ++iter) + LLHostStatus *host_statusp = findHostStatus(host); + if (host_statusp) { - host_statusp = *iter; - if (host_statusp->mHost == host) - { - return (host_statusp->mNumActive); - } + return host_statusp->mNumActive; } return 0; } @@ -372,35 +374,6 @@ BOOL LLXferManager::isLastPacket(S32 packet_num) /////////////////////////////////////////////////////////// -U64 LLXferManager::registerXfer(const void *datap, const S32 length) -{ - LLXfer *xferp; - U64 xfer_id = getNextID(); - - xferp = (LLXfer *) new LLXfer_Mem(); - if (xferp) - { - xferp->mNext = mSendList; - mSendList = xferp; - - xfer_id = ((LLXfer_Mem *)xferp)->registerXfer(xfer_id, datap,length); - - if (!xfer_id) - { - removeXfer(xferp,&mSendList); - } - } - else - { - LL_ERRS() << "Xfer allocation error" << LL_ENDL; - xfer_id = 0; - } - - return(xfer_id); -} - -/////////////////////////////////////////////////////////// - U64 LLXferManager::requestFile(const std::string& local_filename, const std::string& remote_filename, ELLPath remote_path, @@ -411,30 +384,33 @@ U64 LLXferManager::requestFile(const std::string& local_filename, BOOL is_priority, BOOL use_big_packets) { - LLXfer *xferp; + LLXfer_File* file_xfer_p = NULL; - for (xferp = mReceiveList; xferp ; xferp = xferp->mNext) + for (xfer_list_t::iterator iter = mReceiveList.begin(); + iter != mReceiveList.end(); ++iter) { - if (xferp->getXferTypeTag() == LLXfer::XFER_FILE - && (((LLXfer_File*)xferp)->matchesLocalFilename(local_filename)) - && (((LLXfer_File*)xferp)->matchesRemoteFilename(remote_filename, remote_path)) - && (remote_host == xferp->mRemoteHost) - && (callback == xferp->mCallback) - && (user_data == xferp->mCallbackDataHandle)) - + if ((*iter)->getXferTypeTag() == LLXfer::XFER_FILE) { - // cout << "requested a xfer already in progress" << endl; - return xferp->mID; + file_xfer_p = (LLXfer_File*)(*iter); + if (file_xfer_p->matchesLocalFilename(local_filename) + && file_xfer_p->matchesRemoteFilename(remote_filename, remote_path) + && (remote_host == file_xfer_p->mRemoteHost) + && (callback == file_xfer_p->mCallback) + && (user_data == file_xfer_p->mCallbackDataHandle)) + { + // Already have the request (already in progress) + return (*iter)->mID; + } } } U64 xfer_id = 0; S32 chunk_size = use_big_packets ? LL_XFER_LARGE_PAYLOAD : -1; - xferp = (LLXfer *) new LLXfer_File(chunk_size); - if (xferp) + file_xfer_p = new LLXfer_File(chunk_size); + if (file_xfer_p) { - addToList(xferp, mReceiveList, is_priority); + addToList(file_xfer_p, mReceiveList, is_priority); // Remove any file by the same name that happens to be lying // around. @@ -447,7 +423,7 @@ U64 LLXferManager::requestFile(const std::string& local_filename, LLFile::remove(local_filename, ENOENT); } xfer_id = getNextID(); - ((LLXfer_File *)xferp)->initializeRequest( + ((LLXfer_File *)file_xfer_p)->initializeRequest( xfer_id, local_filename, remote_filename, @@ -500,28 +476,32 @@ void LLXferManager::requestVFile(const LLUUID& local_id, void** user_data, BOOL is_priority) { - LLXfer *xferp; + LLXfer_VFile * xfer_p = NULL; - for (xferp = mReceiveList; xferp ; xferp = xferp->mNext) + for (xfer_list_t::iterator iter = mReceiveList.begin(); + iter != mReceiveList.end(); ++iter) { - if (xferp->getXferTypeTag() == LLXfer::XFER_VFILE - && (((LLXfer_VFile*)xferp)->matchesLocalFile(local_id, type)) - && (((LLXfer_VFile*)xferp)->matchesRemoteFile(remote_id, type)) - && (remote_host == xferp->mRemoteHost) - && (callback == xferp->mCallback) - && (user_data == xferp->mCallbackDataHandle)) - + if ((*iter)->getXferTypeTag() == LLXfer::XFER_VFILE) { - // cout << "requested a xfer already in progress" << endl; - return; + xfer_p = (LLXfer_VFile*) (*iter); + if (xfer_p->matchesLocalFile(local_id, type) + && xfer_p->matchesRemoteFile(remote_id, type) + && (remote_host == xfer_p->mRemoteHost) + && (callback == xfer_p->mCallback) + && (user_data == xfer_p->mCallbackDataHandle)) + + { + // Have match, already in progress, don't add a duplicate + return; + } } } - xferp = (LLXfer *) new LLXfer_VFile(); - if (xferp) + xfer_p = new LLXfer_VFile(); + if (xfer_p) { - addToList(xferp, mReceiveList, is_priority); - ((LLXfer_VFile *)xferp)->initializeRequest(getNextID(), + addToList(xfer_p, mReceiveList, is_priority); + ((LLXfer_VFile *)xfer_p)->initializeRequest(getNextID(), vfs, local_id, remote_id, @@ -663,7 +643,7 @@ void LLXferManager::processReceiveData (LLMessageSystem *mesgsys, void ** /*user if (result == LL_ERR_CANNOT_OPEN_FILE) { xferp->abort(LL_ERR_CANNOT_OPEN_FILE); - removeXfer(xferp,&mReceiveList); + removeXfer(xferp,mReceiveList); startPendingDownloads(); return; } @@ -688,7 +668,7 @@ void LLXferManager::processReceiveData (LLMessageSystem *mesgsys, void ** /*user if (isLastPacket(packetnum)) { xferp->processEOF(); - removeXfer(xferp,&mReceiveList); + removeXfer(xferp,mReceiveList); startPendingDownloads(); } } @@ -708,6 +688,7 @@ void LLXferManager::sendConfirmPacket (LLMessageSystem *mesgsys, U64 id, S32 pac mesgsys->addU64Fast(_PREHASH_ID, id); mesgsys->addU32Fast(_PREHASH_Packet, packetnum); + // Ignore a circuit failure here, we'll catch it with another message mesgsys->sendMessage(remote_host); } @@ -825,7 +806,7 @@ void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user LLXfer *xferp; if (uuid != LLUUID::null) - { + { // Request for an asset - use a VFS file if(NULL == LLAssetType::lookup(type)) { LL_WARNS() << "Invalid type for xfer request: " << uuid << ":" @@ -844,8 +825,7 @@ void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user xferp = (LLXfer *)new LLXfer_VFile(mVFS, uuid, type); if (xferp) { - xferp->mNext = mSendList; - mSendList = xferp; + mSendList.push_front(xferp); result = xferp->startSend(id,mesgsys->getSender()); } else @@ -854,7 +834,7 @@ void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user } } else if (!local_filename.empty()) - { + { // Was given a file name to send // See DEV-21775 for detailed security issues if (local_path == LL_PATH_NONE) @@ -912,8 +892,7 @@ void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user if (xferp) { - xferp->mNext = mSendList; - mSendList = xferp; + mSendList.push_front(xferp); result = xferp->startSend(id,mesgsys->getSender()); } else @@ -922,7 +901,7 @@ void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user } } else - { + { // no uuid or filename - use the ID sent char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */ LL_INFOS() << "starting memory transfer: " << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) << " to " @@ -946,7 +925,7 @@ void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user if (xferp) { xferp->abort(result); - removeXfer(xferp,&mSendList); + removeXfer(xferp, mSendList); } else // can happen with a memory transfer not found { @@ -960,24 +939,86 @@ void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user mesgsys->sendMessage(mesgsys->getSender()); } } - else if(xferp && (numActiveXfers(xferp->mRemoteHost) < mMaxOutgoingXfersPerCircuit)) + else if(xferp) { - xferp->sendNextPacket(); - changeNumActiveXfers(xferp->mRemoteHost,1); -// LL_INFOS() << "***STARTING XFER IMMEDIATELY***" << LL_ENDL; - } - else - { - if(xferp) + // Figure out how many transfers the host has requested + LLHostStatus *host_statusp = findHostStatus(xferp->mRemoteHost); + if (host_statusp) { - LL_INFOS() << " queueing xfer request, " << numPendingXfers(xferp->mRemoteHost) << " ahead of this one" << LL_ENDL; + if (host_statusp->mNumActive < mMaxOutgoingXfersPerCircuit) + { // Not many transfers in progress already, so start immediately + xferp->sendNextPacket(); + changeNumActiveXfers(xferp->mRemoteHost,1); + LL_DEBUGS("Xfer") << "Starting xfer ID " << U64_to_str(id) << " immediately" << LL_ENDL; + } + else if (mHardLimitOutgoingXfersPerCircuit == 0 || + (host_statusp->mNumActive + host_statusp->mNumPending) < mHardLimitOutgoingXfersPerCircuit) + { // Must close the file handle and wait for earlier ones to complete + LL_INFOS("Xfer") << " queueing xfer request id " << U64_to_str(id) << ", " + << host_statusp->mNumActive << " active and " + << host_statusp->mNumPending << " pending ahead of this one" + << LL_ENDL; + xferp->closeFileHandle(); // Close the file handle until we're ready to send again + } + else if (mHardLimitOutgoingXfersPerCircuit > 0) + { // Way too many requested ... it's time to stop being nice and kill the circuit + xferp->closeFileHandle(); // Close the file handle in any case + LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(xferp->mRemoteHost); + if (cdp) + { + if (cdp->getTrusted()) + { // Trusted internal circuit - don't kill it + LL_WARNS("Xfer") << "Trusted circuit to " << xferp->mRemoteHost << " has too many xfer requests in the queue " + << host_statusp->mNumActive << " active and " + << host_statusp->mNumPending << " pending ahead of this one" + << LL_ENDL; + } + else + { // Untrusted circuit - time to stop messing around and kill it + LL_WARNS("Xfer") << "Killing circuit to " << xferp->mRemoteHost << " for having too many xfer requests in the queue " + << host_statusp->mNumActive << " active and " + << host_statusp->mNumPending << " pending ahead of this one" + << LL_ENDL; + gMessageSystem->disableCircuit(xferp->mRemoteHost); + } + } + else + { // WTF? Why can't we find a circuit? Try to kill it off + LL_WARNS("Xfer") << "Backlog with circuit to " << xferp->mRemoteHost << " with too many xfer requests in the queue " + << host_statusp->mNumActive << " active and " + << host_statusp->mNumPending << " pending ahead of this one" + << " but no LLCircuitData found???" + << LL_ENDL; + gMessageSystem->disableCircuit(xferp->mRemoteHost); + } + } } else { - LL_WARNS() << "LLXferManager::processFileRequest() - no xfer found!" - << LL_ENDL; + LL_WARNS("Xfer") << "LLXferManager::processFileRequest() - no LLHostStatus found for id " << U64_to_str(id) + << " host " << xferp->mRemoteHost << LL_ENDL; } } + else + { + LL_WARNS("Xfer") << "LLXferManager::processFileRequest() - no xfer found for id " << U64_to_str(id) << LL_ENDL; + } +} + +/////////////////////////////////////////////////////////// + +// Return true if host is in a transfer-flood sitation. Same check for both internal and external hosts +bool LLXferManager::isHostFlooded(const LLHost & host) +{ + bool flooded = false; + LLHostStatus *host_statusp = findHostStatus(host); + if (host_statusp) + { + flooded = (mHardLimitOutgoingXfersPerCircuit > 0 && + (host_statusp->mNumActive + host_statusp->mNumPending) >= (S32)(mHardLimitOutgoingXfersPerCircuit * 0.8f)); + } + + return flooded; } @@ -1002,52 +1043,57 @@ void LLXferManager::processConfirmation (LLMessageSystem *mesgsys, void ** /*use } else { - removeXfer(xferp, &mSendList); + removeXfer(xferp, mSendList); } } } /////////////////////////////////////////////////////////// -void LLXferManager::retransmitUnackedPackets () +void LLXferManager::retransmitUnackedPackets() { LLXfer *xferp; - LLXfer *delp; - xferp = mReceiveList; - while(xferp) + + xfer_list_t::iterator iter = mReceiveList.begin(); + while (iter != mReceiveList.end()) { + xferp = (*iter); if (xferp->mStatus == e_LL_XFER_IN_PROGRESS) { // if the circuit dies, abort if (! gMessageSystem->mCircuitInfo.isCircuitAlive( xferp->mRemoteHost )) { - LL_INFOS() << "Xfer found in progress on dead circuit, aborting" << LL_ENDL; + LL_WARNS("Xfer") << "Xfer found in progress on dead circuit, aborting transfer to " + << xferp->mRemoteHost.getIPandPort() + << LL_ENDL; xferp->mCallbackResult = LL_ERR_CIRCUIT_GONE; xferp->processEOF(); - delp = xferp; - xferp = xferp->mNext; - removeXfer(delp,&mReceiveList); + + iter = mReceiveList.erase(iter); // iter is set to next one after the deletion point + delete (xferp); continue; } } - xferp = xferp->mNext; + ++iter; } - xferp = mSendList; updateHostStatus(); + F32 et; - while (xferp) + iter = mSendList.begin(); + while (iter != mSendList.end()) { + xferp = (*iter); if (xferp->mWaitingForACK && ( (et = xferp->ACKTimer.getElapsedTimeF32()) > LL_PACKET_TIMEOUT)) { if (xferp->mRetries > LL_PACKET_RETRY_LIMIT) { LL_INFOS() << "dropping xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << " packet retransmit limit exceeded, xfer dropped" << LL_ENDL; xferp->abort(LL_ERR_TCP_TIMEOUT); - delp = xferp; - xferp = xferp->mNext; - removeXfer(delp,&mSendList); + iter = mSendList.erase(iter); + delete xferp; + continue; } else { @@ -1060,25 +1106,37 @@ void LLXferManager::retransmitUnackedPackets () { LL_INFOS() << "registered xfer never requested, xfer dropped" << LL_ENDL; xferp->abort(LL_ERR_TCP_TIMEOUT); - delp = xferp; - xferp = xferp->mNext; - removeXfer(delp,&mSendList); + iter = mSendList.erase(iter); + delete xferp; + continue; } else if (xferp->mStatus == e_LL_XFER_ABORTED) { LL_WARNS() << "Removing aborted xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << LL_ENDL; - delp = xferp; - xferp = xferp->mNext; - removeXfer(delp,&mSendList); + iter = mSendList.erase(iter); + delete xferp; + continue; } else if (xferp->mStatus == e_LL_XFER_PENDING) { // LL_INFOS() << "*** numActiveXfers = " << numActiveXfers(xferp->mRemoteHost) << " mMaxOutgoingXfersPerCircuit = " << mMaxOutgoingXfersPerCircuit << LL_ENDL; if (numActiveXfers(xferp->mRemoteHost) < mMaxOutgoingXfersPerCircuit) { -// LL_INFOS() << "bumping pending xfer to active" << LL_ENDL; - xferp->sendNextPacket(); - changeNumActiveXfers(xferp->mRemoteHost,1); + if (xferp->reopenFileHandle()) + { + LL_WARNS("Xfer") << "Error re-opening file handle for xfer ID " << U64_to_str(xferp->mID) + << " to host " << xferp->mRemoteHost << LL_ENDL; + xferp->abort(LL_ERR_CANNOT_OPEN_FILE); + iter = mSendList.erase(iter); + delete xferp; + continue; + } + else + { // No error re-opening the file, send the first packet + LL_DEBUGS("Xfer") << "Moving pending xfer ID " << U64_to_str(xferp->mID) << " to active" << LL_ENDL; + xferp->sendNextPacket(); + changeNumActiveXfers(xferp->mRemoteHost,1); + } } xferp = xferp->mNext; } @@ -1124,7 +1182,7 @@ void LLXferManager::abortRequestById(U64 xfer_id, S32 result_code) { xferp->mCallbackResult = result_code; xferp->processEOF(); //should notify requester - removeXfer(xferp, &mReceiveList); + removeXfer(xferp, mReceiveList); } // Since already removed or marked as aborted no need // to wait for processAbort() to start new download @@ -1148,7 +1206,7 @@ void LLXferManager::processAbort (LLMessageSystem *mesgsys, void ** /*user_data* { xferp->mCallbackResult = result_code; xferp->processEOF(); - removeXfer(xferp, &mReceiveList); + removeXfer(xferp, mReceiveList); startPendingDownloads(); } } @@ -1164,12 +1222,15 @@ void LLXferManager::startPendingDownloads() // requests get pushed toward the back. Thus, if we didn't do a // stateful iteration, it would be possible for old requests to // never start. - LLXfer* xferp = mReceiveList; + LLXfer* xferp; std::list pending_downloads; S32 download_count = 0; S32 pending_count = 0; - while(xferp) + for (xfer_list_t::iterator iter = mReceiveList.begin(); + iter != mReceiveList.end(); + ++iter) { + xferp = (*iter); if(xferp->mStatus == e_LL_XFER_PENDING) { ++pending_count; @@ -1209,29 +1270,15 @@ void LLXferManager::startPendingDownloads() /////////////////////////////////////////////////////////// -void LLXferManager::addToList(LLXfer* xferp, LLXfer*& head, BOOL is_priority) +void LLXferManager::addToList(LLXfer* xferp, xfer_list_t & xfer_list, BOOL is_priority) { if(is_priority) { - xferp->mNext = NULL; - LLXfer* next = head; - if(next) - { - while(next->mNext) - { - next = next->mNext; - } - next->mNext = xferp; - } - else - { - head = xferp; - } + xfer_list.push_back(xferp); } else { - xferp->mNext = head; - head = xferp; + xfer_list.push_front(xferp); } } diff --git a/indra/llmessage/llxfermanager.h b/indra/llmessage/llxfermanager.h index d258f0a5ce..ebe8e57ee2 100644 --- a/indra/llmessage/llxfermanager.h +++ b/indra/llmessage/llxfermanager.h @@ -77,6 +77,7 @@ class LLXferManager protected: S32 mMaxOutgoingXfersPerCircuit; + S32 mHardLimitOutgoingXfersPerCircuit; // At this limit, kill off the connection S32 mMaxIncomingXfers; BOOL mUseAckThrottling; // Use ack throttling to cap file xfer bandwidth @@ -92,8 +93,10 @@ class LLXferManager HIGH_PRIORITY = TRUE, }; - LLXfer *mSendList; - LLXfer *mReceiveList; + // Linked FIFO list, add to the front and pull from back + typedef std::deque xfer_list_t; + xfer_list_t mSendList; + xfer_list_t mReceiveList; typedef std::list status_list_t; status_list_t mOutgoingHosts; @@ -102,7 +105,7 @@ class LLXferManager protected: // implementation methods virtual void startPendingDownloads(); - virtual void addToList(LLXfer* xferp, LLXfer*& head, BOOL is_priority); + virtual void addToList(LLXfer* xferp, xfer_list_t & list, BOOL is_priority); std::multiset mExpectedTransfers; // files that are authorized to transfer out std::multiset mExpectedRequests; // files that are authorized to be downloaded on top of @@ -117,14 +120,18 @@ class LLXferManager void setAckThrottleBPS(const F32 bps); // list management routines - virtual LLXfer *findXfer(U64 id, LLXfer *list_head); - virtual void removeXfer (LLXfer *delp, LLXfer **list_head); + virtual LLXfer *findXfer(U64 id, xfer_list_t & xfer_list); + virtual void removeXfer (LLXfer *delp, xfer_list_t & xfer_list); + + LLHostStatus * findHostStatus(const LLHost &host); virtual U32 numActiveListEntries(LLXfer *list_head); virtual S32 numActiveXfers(const LLHost &host); virtual S32 numPendingXfers(const LLHost &host); + virtual void changeNumActiveXfers(const LLHost &host, S32 delta); virtual void setMaxOutgoingXfersPerCircuit (S32 max_num); + virtual void setHardLimitOutgoingXfersPerCircuit(S32 max_num); virtual void setMaxIncomingXfers(S32 max_num); virtual void updateHostStatus(); virtual void printHostStatus(); @@ -136,8 +143,6 @@ class LLXferManager virtual S32 decodePacketNum(S32 packet_num); virtual BOOL isLastPacket(S32 packet_num); - virtual U64 registerXfer(const void *datap, const S32 length); - // file requesting routines // .. to file virtual U64 requestFile(const std::string& local_filename, @@ -204,6 +209,8 @@ class LLXferManager // error handling void abortRequestById(U64 xfer_id, S32 result_code); virtual void processAbort (LLMessageSystem *mesgsys, void **user_data); + + virtual bool isHostFlooded(const LLHost & host); }; extern LLXferManager* gXferManager; From 46cc8496f0c2405342b0892a673bcf69cc13847b Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Wed, 2 May 2018 12:24:14 +0300 Subject: [PATCH 06/20] MAINT-8553 Allow copy a link or copy a folder containing a link in inventory. --- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llinventorybridge.cpp | 26 ++++++++++++------------- indra/newview/llinventoryfunctions.cpp | 6 +++++- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 45e2fc9bf0..379c05a66c 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4798,7 +4798,7 @@ Type Boolean Value - 0 + 1 InventoryOutboxDisplayBoth diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 67b51f11b3..29e63e4bca 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -779,6 +779,14 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, if (obj) { + + items.push_back(std::string("Copy Separator")); + items.push_back(std::string("Copy")); + if (!isItemCopyable()) + { + disabled_items.push_back(std::string("Copy")); + } + if (obj->getIsLinkType()) { items.push_back(std::string("Find Original")); @@ -821,13 +829,6 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Copy Asset UUID")); } } - items.push_back(std::string("Copy Separator")); - - items.push_back(std::string("Copy")); - if (!isItemCopyable()) - { - disabled_items.push_back(std::string("Copy")); - } items.push_back(std::string("Cut")); if (!isItemMovable() || !isItemRemovable()) @@ -2104,12 +2105,6 @@ BOOL LLItemBridge::isItemCopyable() const return FALSE; } - // You can never copy a link. - if (item->getIsLinkType()) - { - return FALSE; - } - return item->getPermissions().allowCopyBy(gAgent.getID()) || gSavedSettings.getBOOL("InventoryLinking"); } return FALSE; @@ -3812,6 +3807,11 @@ void LLFolderBridge::perform_pasteFromClipboard() break; } } + else if (item->getIsLinkType()) + { + link_inventory_object(parent_id, item_id, + LLPointer(NULL)); + } else { copy_inventory_item( diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index e056ccebee..4914490319 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -425,7 +425,11 @@ void copy_inventory_category(LLInventoryModel* model, LLInventoryItem* item = *iter; LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_uuid)); - if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + if (item->getIsLinkType()) + { + link_inventory_object(new_cat_uuid, item->getLinkedUUID(), cb); + } + else if(!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) { // If the item is nocopy, we do nothing or, optionally, move it if (move_no_copy_items) From 571d2ebe1c13b8645653a4d335db8a5a671dbaa3 Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Wed, 2 May 2018 12:30:22 +0300 Subject: [PATCH 07/20] MAINT-8531 Remove viewer UI which supports buying items out of task inventory --- indra/newview/llpanelobjectinventory.cpp | 228 +----------------- .../skins/default/xui/en/menu_inventory.xml | 8 - 2 files changed, 7 insertions(+), 229 deletions(-) diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 6afd729a76..cd1875e995 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -101,9 +101,7 @@ public: static LLTaskInvFVBridge* createObjectBridge(LLPanelObjectInventory* panel, LLInventoryObject* object); void showProperties(); - void buyItem(); S32 getPrice(); - static bool commitBuyItem(const LLSD& notification, const LLSD& response); // LLFolderViewModelItemInventory functionality virtual const std::string& getName() const; @@ -200,75 +198,6 @@ void LLTaskInvFVBridge::showProperties() show_task_item_profile(mUUID, mPanel->getTaskUUID()); } -struct LLBuyInvItemData -{ - LLUUID mTaskID; - LLUUID mItemID; - LLAssetType::EType mType; - - LLBuyInvItemData(const LLUUID& task, - const LLUUID& item, - LLAssetType::EType type) : - mTaskID(task), mItemID(item), mType(type) - {} -}; - -void LLTaskInvFVBridge::buyItem() -{ - LL_INFOS() << "LLTaskInvFVBridge::buyItem()" << LL_ENDL; - LLInventoryItem* item = findItem(); - if(!item || !item->getSaleInfo().isForSale()) return; - LLBuyInvItemData* inv = new LLBuyInvItemData(mPanel->getTaskUUID(), - mUUID, - item->getType()); - - const LLSaleInfo& sale_info = item->getSaleInfo(); - const LLPermissions& perm = item->getPermissions(); - const std::string owner_name; // no owner name currently... FIXME? - - LLViewerObject* obj; - if( ( obj = gObjectList.findObject( mPanel->getTaskUUID() ) ) && obj->isAttachment() ) - { - LLNotificationsUtil::add("Cannot_Purchase_an_Attachment"); - LL_INFOS() << "Attempt to purchase an attachment" << LL_ENDL; - delete inv; - } - else - { - LLSD args; - args["PRICE"] = llformat("%d",sale_info.getSalePrice()); - args["OWNER"] = owner_name; - if (sale_info.getSaleType() != LLSaleInfo::FS_CONTENTS) - { - U32 next_owner_mask = perm.getMaskNextOwner(); - args["MODIFYPERM"] = LLTrans::getString((next_owner_mask & PERM_MODIFY) ? "PermYes" : "PermNo"); - args["COPYPERM"] = LLTrans::getString((next_owner_mask & PERM_COPY) ? "PermYes" : "PermNo"); - args["RESELLPERM"] = LLTrans::getString((next_owner_mask & PERM_TRANSFER) ? "PermYes" : "PermNo"); - } - - std::string alertdesc; - switch(sale_info.getSaleType()) - { - case LLSaleInfo::FS_ORIGINAL: - alertdesc = owner_name.empty() ? "BuyOriginalNoOwner" : "BuyOriginal"; - break; - case LLSaleInfo::FS_CONTENTS: - alertdesc = owner_name.empty() ? "BuyContentsNoOwner" : "BuyContents"; - break; - case LLSaleInfo::FS_COPY: - default: - alertdesc = owner_name.empty() ? "BuyCopyNoOwner" : "BuyCopy"; - break; - } - - LLSD payload; - payload["task_id"] = inv->mTaskID; - payload["item_id"] = inv->mItemID; - payload["type"] = inv->mType; - LLNotificationsUtil::add(alertdesc, args, payload, LLTaskInvFVBridge::commitBuyItem); - } -} - S32 LLTaskInvFVBridge::getPrice() { LLInventoryItem* item = findItem(); @@ -282,31 +211,6 @@ S32 LLTaskInvFVBridge::getPrice() } } -// static -bool LLTaskInvFVBridge::commitBuyItem(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if(0 == option) - { - LLViewerObject* object = gObjectList.findObject(notification["payload"]["task_id"].asUUID()); - if(!object || !object->getRegion()) return false; - - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_BuyObjectInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_Data); - msg->addUUIDFast(_PREHASH_ObjectID, notification["payload"]["task_id"].asUUID()); - msg->addUUIDFast(_PREHASH_ItemID, notification["payload"]["item_id"].asUUID()); - msg->addUUIDFast(_PREHASH_FolderID, - gInventory.findCategoryUUIDForType((LLFolderType::EType)notification["payload"]["type"].asInteger())); - msg->sendReliable(object->getRegion()->getHost()); - } - return false; -} - const std::string& LLTaskInvFVBridge::getName() const { return mName; @@ -615,29 +519,7 @@ BOOL LLTaskInvFVBridge::dragOrDrop(MASK mask, BOOL drop, // virtual void LLTaskInvFVBridge::performAction(LLInventoryModel* model, std::string action) { - if (action == "task_buy") - { - // Check the price of the item. - S32 price = getPrice(); - if (-1 == price) - { - LL_WARNS() << "label_buy_task_bridged_item: Invalid price" << LL_ENDL; - } - else - { - if (price > 0 && price > gStatusBar->getBalance()) - { - LLStringUtil::format_map_t args; - args["AMOUNT"] = llformat("%d", price); - LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("this_costs", args), price ); - } - else - { - buyItem(); - } - } - } - else if (action == "task_open") + if (action == "task_open") { openItem(); } @@ -659,39 +541,7 @@ void LLTaskInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) return; } - if(!gAgent.allowOperation(PERM_OWNER, item->getPermissions(), - GP_OBJECT_MANIPULATE) - && item->getSaleInfo().isForSale()) - { - items.push_back(std::string("Task Buy")); - - std::string label= LLTrans::getString("Buy"); - // Check the price of the item. - S32 price = getPrice(); - if (-1 == price) - { - LL_WARNS() << "label_buy_task_bridged_item: Invalid price" << LL_ENDL; - } - else - { - std::ostringstream info; - info << LLTrans::getString("BuyforL$") << price; - label.assign(info.str()); - } - - const LLView::child_list_t *list = menu.getChildList(); - LLView::child_list_t::const_iterator itor; - for (itor = list->begin(); itor != list->end(); ++itor) - { - std::string name = (*itor)->getName(); - LLMenuItemCallGL* menu_itemp = dynamic_cast(*itor); - if (name == "Task Buy" && menu_itemp) - { - menu_itemp->setLabel(label); - } - } - } - else if (canOpenItem()) + if (canOpenItem()) { items.push_back(std::string("Task Open")); } @@ -986,38 +836,7 @@ void LLTaskSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) return; } - if(item->getPermissions().getOwner() != gAgent.getID() - && item->getSaleInfo().isForSale()) - { - items.push_back(std::string("Task Buy")); - - std::string label= LLTrans::getString("Buy"); - // Check the price of the item. - S32 price = getPrice(); - if (-1 == price) - { - LL_WARNS() << "label_buy_task_bridged_item: Invalid price" << LL_ENDL; - } - else - { - std::ostringstream info; - info << LLTrans::getString("BuyforL$") << price; - label.assign(info.str()); - } - - const LLView::child_list_t *list = menu.getChildList(); - LLView::child_list_t::const_iterator itor; - for (itor = list->begin(); itor != list->end(); ++itor) - { - std::string name = (*itor)->getName(); - LLMenuItemCallGL* menu_itemp = dynamic_cast(*itor); - if (name == "Task Buy" && menu_itemp) - { - menu_itemp->setLabel(label); - } - } - } - else if (canOpenItem()) + if (canOpenItem()) { if (!isItemCopyable()) { @@ -1364,45 +1183,12 @@ void LLTaskMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) return; } - if(item->getPermissions().getOwner() != gAgent.getID() - && item->getSaleInfo().isForSale()) + items.push_back(std::string("Task Open")); + if (!isItemCopyable()) { - items.push_back(std::string("Task Buy")); - - std::string label= LLTrans::getString("Buy"); - // Check the price of the item. - S32 price = getPrice(); - if (-1 == price) - { - LL_WARNS() << "label_buy_task_bridged_item: Invalid price" << LL_ENDL; - } - else - { - std::ostringstream info; - info << LLTrans::getString("BuyforL$") << price; - label.assign(info.str()); - } - - const LLView::child_list_t *list = menu.getChildList(); - LLView::child_list_t::const_iterator itor; - for (itor = list->begin(); itor != list->end(); ++itor) - { - std::string name = (*itor)->getName(); - LLMenuItemCallGL* menu_itemp = dynamic_cast(*itor); - if (name == "Task Buy" && menu_itemp) - { - menu_itemp->setLabel(label); - } - } - } - else - { - items.push_back(std::string("Task Open")); - if (!isItemCopyable()) - { - disabled_items.push_back(std::string("Task Open")); - } + disabled_items.push_back(std::string("Task Open")); } + items.push_back(std::string("Task Properties")); if ((flags & FIRST_SELECTED_ITEM) == 0) { diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 5b45364127..3ea9e77a03 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -87,14 +87,6 @@ - - - Date: Wed, 2 May 2018 16:15:43 +0300 Subject: [PATCH 08/20] fix line endings --- indra/newview/llinventorybridge.cpp | 8 ++++---- indra/newview/llinventoryfunctions.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 29e63e4bca..90a000c196 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -3807,10 +3807,10 @@ void LLFolderBridge::perform_pasteFromClipboard() break; } } - else if (item->getIsLinkType()) - { - link_inventory_object(parent_id, item_id, - LLPointer(NULL)); + else if (item->getIsLinkType()) + { + link_inventory_object(parent_id, item_id, + LLPointer(NULL)); } else { diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 4914490319..0da4331af8 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -425,9 +425,9 @@ void copy_inventory_category(LLInventoryModel* model, LLInventoryItem* item = *iter; LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_uuid)); - if (item->getIsLinkType()) - { - link_inventory_object(new_cat_uuid, item->getLinkedUUID(), cb); + if (item->getIsLinkType()) + { + link_inventory_object(new_cat_uuid, item->getLinkedUUID(), cb); } else if(!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) { From 85df4c30315500b7bd6e420248fd9174ad6c8570 Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Wed, 2 May 2018 18:34:59 +0300 Subject: [PATCH 09/20] MAINT-8616 FIXED Edit Tool often displays the wrong Creator and/or Owner of an object --- indra/newview/llpanelpermissions.cpp | 75 +++++++++++++++++++++------- indra/newview/llpanelpermissions.h | 12 ++++- 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index 29ca172f60..0e5a7aec0d 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -171,6 +171,8 @@ BOOL LLPanelPermissions::postBuild() childSetCommitCallback("search_check",LLPanelPermissions::onCommitIncludeInSearch,this); mLabelGroupName = getChild("Group Name Proxy"); + mLabelOwnerName = getChild("Owner Name"); + mLabelCreatorName = getChild("Creator Name"); return TRUE; } @@ -178,6 +180,14 @@ BOOL LLPanelPermissions::postBuild() LLPanelPermissions::~LLPanelPermissions() { + if (mOwnerCacheConnection.connected()) + { + mOwnerCacheConnection.disconnect(); + } + if (mCreatorCacheConnection.connected()) + { + mCreatorCacheConnection.disconnect(); + } // base class will take care of everything } @@ -192,14 +202,14 @@ void LLPanelPermissions::disableAll() getChildView("Creator:")->setEnabled(FALSE); getChild("Creator Icon")->setVisible(FALSE); - getChild("Creator Name")->setValue(LLStringUtil::null); - getChildView("Creator Name")->setEnabled(FALSE); + mLabelCreatorName->setValue(LLStringUtil::null); + mLabelCreatorName->setEnabled(FALSE); getChildView("Owner:")->setEnabled(FALSE); getChild("Owner Icon")->setVisible(FALSE); getChild("Owner Group Icon")->setVisible(FALSE); - getChild("Owner Name")->setValue(LLStringUtil::null); - getChildView("Owner Name")->setEnabled(FALSE); + mLabelOwnerName->setValue(LLStringUtil::null); + mLabelOwnerName->setEnabled(FALSE); getChildView("Group:")->setEnabled(FALSE); getChild("Group Name Proxy")->setValue(LLStringUtil::null); @@ -383,7 +393,7 @@ void LLPanelPermissions::refresh() style_params.color = link_color; style_params.readonly_color = link_color; style_params.is_link = true; // link will be added later - const LLFontGL* fontp = getChild("Creator Name")->getFont(); + const LLFontGL* fontp = mLabelCreatorName->getFont(); style_params.font.name = LLFontGL::nameFromFont(fontp); style_params.font.size = LLFontGL::sizeFromFont(fontp); style_params.font.style = "UNDERLINE"; @@ -391,14 +401,20 @@ void LLPanelPermissions::refresh() LLAvatarName av_name; if (LLAvatarNameCache::get(mCreatorID, &av_name)) { - // If name isn't present, this will 'request' it and trigger refresh() again - LLTextBox* text_box = getChild("Creator Name"); - style_params.link_href = creator_app_link; - text_box->setText(av_name.getCompleteName(), style_params); + updateCreatorName(mCreatorID, av_name, style_params); + } + else + { + if (mCreatorCacheConnection.connected()) + { + mCreatorCacheConnection.disconnect(); + } + mLabelCreatorName->setText(LLTrans::getString("None")); + mCreatorCacheConnection = LLAvatarNameCache::get(mCreatorID, boost::bind(&LLPanelPermissions::updateCreatorName, this, _1, _2, style_params)); } getChild("Creator Icon")->setValue(mCreatorID); getChild("Creator Icon")->setVisible(TRUE); - getChildView("Creator Name")->setEnabled(TRUE); + mLabelCreatorName->setEnabled(TRUE); // Update owner text field getChildView("Owner:")->setEnabled(TRUE); @@ -413,9 +429,8 @@ void LLPanelPermissions::refresh() LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(mOwnerID); if (group_data && group_data->isGroupPropertiesDataComplete()) { - LLTextBox* text_box = getChild("Owner Name"); style_params.link_href = owner_app_link; - text_box->setText(group_data->mName, style_params); + mLabelOwnerName->setText(group_data->mName, style_params); getChild("Owner Group Icon")->setIconId(group_data->mInsigniaID); getChild("Owner Group Icon")->setVisible(TRUE); getChild("Owner Icon")->setVisible(FALSE); @@ -444,18 +459,27 @@ void LLPanelPermissions::refresh() } owner_id = mLastOwnerID; } + + style_params.link_href = owner_app_link; if (LLAvatarNameCache::get(owner_id, &av_name)) { - // If name isn't present, this will 'request' it and trigger refresh() again - LLTextBox* text_box = getChild("Owner Name"); - style_params.link_href = owner_app_link; - text_box->setText(av_name.getCompleteName(), style_params); + updateOwnerName(owner_id, av_name, style_params); } + else + { + if (mOwnerCacheConnection.connected()) + { + mOwnerCacheConnection.disconnect(); + } + mLabelOwnerName->setText(LLTrans::getString("None")); + mOwnerCacheConnection = LLAvatarNameCache::get(owner_id, boost::bind(&LLPanelPermissions::updateOwnerName, this, _1, _2, style_params)); + } + getChild("Owner Icon")->setValue(owner_id); getChild("Owner Icon")->setVisible(TRUE); getChild("Owner Group Icon")->setVisible(FALSE); } - getChildView("Owner Name")->setEnabled(TRUE); + mLabelOwnerName->setEnabled(TRUE); // update group text field getChildView("Group:")->setEnabled(TRUE); @@ -944,6 +968,23 @@ void LLPanelPermissions::refresh() getChildView("clickaction")->setEnabled(is_perm_modify && is_nonpermanent_enforced && all_volume); } +void LLPanelPermissions::updateOwnerName(const LLUUID& owner_id, const LLAvatarName& owner_name, const LLStyle::Params& style_params) +{ + if (mOwnerCacheConnection.connected()) + { + mOwnerCacheConnection.disconnect(); + } + mLabelOwnerName->setText(owner_name.getCompleteName(), style_params); +} + +void LLPanelPermissions::updateCreatorName(const LLUUID& creator_id, const LLAvatarName& creator_name, const LLStyle::Params& style_params) +{ + if (mCreatorCacheConnection.connected()) + { + mCreatorCacheConnection.disconnect(); + } + mLabelCreatorName->setText(creator_name.getCompleteName(), style_params); +} // static void LLPanelPermissions::onClickClaim(void*) diff --git a/indra/newview/llpanelpermissions.h b/indra/newview/llpanelpermissions.h index 2d15954baa..e657f8f8b7 100644 --- a/indra/newview/llpanelpermissions.h +++ b/indra/newview/llpanelpermissions.h @@ -28,6 +28,7 @@ #define LL_LLPANELPERMISSIONS_H #include "llpanel.h" +#include "llstyle.h" #include "lluuid.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -36,6 +37,8 @@ // Panel for permissions of an object. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLAvatarName; +class LLTextBox; class LLNameBox; class LLViewerInventoryItem; @@ -46,7 +49,8 @@ public: virtual ~LLPanelPermissions(); /*virtual*/ BOOL postBuild(); - + void updateOwnerName(const LLUUID& owner_id, const LLAvatarName& owner_name, const LLStyle::Params& style_params); + void updateCreatorName(const LLUUID& creator_id, const LLAvatarName& creator_name, const LLStyle::Params& style_params); void refresh(); // refresh all labels as needed protected: @@ -85,10 +89,14 @@ protected: private: LLNameBox* mLabelGroupName; // group name - + LLTextBox* mLabelOwnerName; + LLTextBox* mLabelCreatorName; LLUUID mCreatorID; LLUUID mOwnerID; LLUUID mLastOwnerID; + + boost::signals2::connection mOwnerCacheConnection; + boost::signals2::connection mCreatorCacheConnection; }; From 43335283bd2aaa9934f152cf95840f722a9d841e Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Thu, 3 May 2018 17:55:17 +0300 Subject: [PATCH 10/20] MAINT-1421 FIXED "Show Memory" doesn't display memory stats on mac --- indra/newview/llviewerwindow.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 7e90a35f88..d54138b80e 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -391,14 +391,12 @@ public: } } -#if LL_WINDOWS if (gSavedSettings.getBOOL("DebugShowMemory")) { addText(xpos, ypos, STRINGIZE("Memory: " << (LLMemory::getCurrentRSS() / 1024) << " (KB)")); ypos += y_inc; } -#endif if (gDisplayCameraPos) { From 7b5c0dd39a0928601a0e0ae51d4a75ac7f254ab3 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 3 May 2018 18:44:10 +0000 Subject: [PATCH 11/20] MAINT-8593 Viewer should not repeat loads indefinetely --- indra/newview/llmeshrepository.cpp | 368 +++++++++++++++++++---------- indra/newview/llmeshrepository.h | 52 +++- 2 files changed, 279 insertions(+), 141 deletions(-) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index bb64201fef..4011328104 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -353,6 +353,9 @@ const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes const long SMALL_MESH_XFER_TIMEOUT = 120L; // Seconds to complete xfer, small mesh downloads const long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large downloads +const U32 DOWNLOAD_RETRY_LIMIT = 8; +const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds + // Would normally like to retry on uploads as some // retryable failures would be recoverable. Unfortunately, // the mesh service is using 500 (retryable) rather than @@ -516,6 +519,24 @@ void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res, } } +void RequestStats::updateTime() +{ + U32 modifier = 1 << mRetries; // before ++ + mRetries++; + mTimer.reset(); + mTimer.setTimerExpirySec(DOWNLOAD_RETRY_DELAY * (F32)modifier); // up to 32s, 64 total wait +} + +bool RequestStats::canRetry() const +{ + return mRetries < DOWNLOAD_RETRY_LIMIT; +} + +bool RequestStats::isDelayed() const +{ + return mTimer.getStarted() && !mTimer.hasExpired(); +} + LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMaterial& material) { LLPointer< LLViewerFetchedTexture > * ppTex = static_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData); @@ -890,142 +911,225 @@ void LLMeshRepoThread::run() sRequestWaterLevel = mHttpRequestSet.size(); // Stats data update // NOTE: order of queue processing intentionally favors LOD requests over header requests + // Todo: we are processing mLODReqQ, mHeaderReqQ, mSkinRequests, mDecompositionRequests and mPhysicsShapeRequests + // in relatively similar manners, remake code to simplify/unify the process, + // like processRequests(&requestQ, fetchFunction); which does same thing for each element - while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) - { - if (! mMutex) - { - break; - } - mMutex->lock(); - LODRequest req = mLODReqQ.front(); - mLODReqQ.pop(); - LLMeshRepository::sLODProcessing--; - mMutex->unlock(); + if (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) + { + std::list incomplete; + while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) + { + if (!mMutex) + { + break; + } - // Todo: this and other cases shouldn't retry indefinitely, at the very least do as with mDecompositionRequests - if (!fetchMeshLOD(req.mMeshParams, req.mLOD)) // failed, resubmit - { - mMutex->lock(); - mLODReqQ.push(req); - ++LLMeshRepository::sLODProcessing; - mMutex->unlock(); - } - } + mMutex->lock(); + LODRequest req = mLODReqQ.front(); + mLODReqQ.pop(); + LLMeshRepository::sLODProcessing--; + mMutex->unlock(); + if (req.isDelayed()) + { + // failed to load before, wait a bit + incomplete.push_front(req); + } + else if (!fetchMeshLOD(req.mMeshParams, req.mLOD, req.canRetry())) + { + if (req.canRetry()) + { + // failed, resubmit + req.updateTime(); + incomplete.push_front(req); + } + else + { + // too many fails + mUnavailableQ.push(req); + LL_WARNS() << "Failed to load " << req.mMeshParams << " , skip" << LL_ENDL; + } + } + } - while (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) - { - if (! mMutex) - { - break; - } - mMutex->lock(); - HeaderRequest req = mHeaderReqQ.front(); - mHeaderReqQ.pop(); - mMutex->unlock(); - if (!fetchMeshHeader(req.mMeshParams))//failed, resubmit - { - mMutex->lock(); - mHeaderReqQ.push(req) ; - mMutex->unlock(); - } - } + if (!incomplete.empty()) + { + LLMutexLock locker(mMutex); + for (std::list::iterator iter = incomplete.begin(); iter != incomplete.end(); iter++) + { + mLODReqQ.push(*iter); + ++LLMeshRepository::sLODProcessing; + } + } + } - // For the final three request lists, similar goal to above but - // slightly different queue structures. Stay off the mutex when - // performing long-duration actions. + if (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) + { + std::list incomplete; + while (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) + { + if (!mMutex) + { + break; + } - if (mHttpRequestSet.size() < sRequestHighWater - && (! mSkinRequests.empty() - || ! mDecompositionRequests.empty() - || ! mPhysicsShapeRequests.empty())) - { - // Something to do probably, lock and double-check. We don't want - // to hold the lock long here. That will stall main thread activities - // so we bounce it. + mMutex->lock(); + HeaderRequest req = mHeaderReqQ.front(); + mHeaderReqQ.pop(); + mMutex->unlock(); + if (req.isDelayed()) + { + // failed to load before, wait a bit + incomplete.push_front(req); + } + else if (!fetchMeshHeader(req.mMeshParams, req.getRetries())) + { + if (req.canRetry()) + { + //failed, resubmit + req.updateTime(); + incomplete.push_front(req); + } + else + { + LL_DEBUGS() << "mHeaderReqQ failed: " << req.mMeshParams << LL_ENDL; + } + } + } - mMutex->lock(); - if (! mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) - { - std::set incomplete; - std::set::iterator iter(mSkinRequests.begin()); - while (iter != mSkinRequests.end() && mHttpRequestSet.size() < sRequestHighWater) - { - LLUUID mesh_id = *iter; - mSkinRequests.erase(iter); - mMutex->unlock(); + if (!incomplete.empty()) + { + LLMutexLock locker(mMutex); + for (std::list::iterator iter = incomplete.begin(); iter != incomplete.end(); iter++) + { + mHeaderReqQ.push(*iter); + } + } + } - if (! fetchMeshSkinInfo(mesh_id)) - { - incomplete.insert(mesh_id); - } + // For the final three request lists, similar goal to above but + // slightly different queue structures. Stay off the mutex when + // performing long-duration actions. - mMutex->lock(); - iter = mSkinRequests.begin(); - } + if (mHttpRequestSet.size() < sRequestHighWater + && (!mSkinRequests.empty() + || !mDecompositionRequests.empty() + || !mPhysicsShapeRequests.empty())) + { + // Something to do probably, lock and double-check. We don't want + // to hold the lock long here. That will stall main thread activities + // so we bounce it. - if (! incomplete.empty()) - { - mSkinRequests.insert(incomplete.begin(), incomplete.end()); - } - } + if (!mSkinRequests.empty()) + { + std::set incomplete; + while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) + { + mMutex->lock(); + std::set::iterator iter = mSkinRequests.begin(); + UUIDBasedRequest req = *iter; + mSkinRequests.erase(iter); + mMutex->unlock(); + if (req.isDelayed()) + { + incomplete.insert(req); + } + else if (!fetchMeshSkinInfo(req.mId)) + { + if (req.canRetry()) + { + req.updateTime(); + incomplete.insert(req); + } + else + { + LL_DEBUGS() << "mSkinRequests failed: " << req.mId << LL_ENDL; + } + } + } - // holding lock, try next list - // *TODO: For UI/debug-oriented lists, we might drop the fine- - // grained locking as there's a lowered expectation of smoothness - // in these cases. - if (! mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) - { - std::set incomplete; - std::set::iterator iter(mDecompositionRequests.begin()); - while (iter != mDecompositionRequests.end() && mHttpRequestSet.size() < sRequestHighWater) - { - LLUUID mesh_id = *iter; - mDecompositionRequests.erase(iter); - mMutex->unlock(); - - if (! fetchMeshDecomposition(mesh_id)) - { - incomplete.insert(mesh_id); - } + if (!incomplete.empty()) + { + LLMutexLock locker(mMutex); + mSkinRequests.insert(incomplete.begin(), incomplete.end()); + } + } - mMutex->lock(); - iter = mDecompositionRequests.begin(); - } + // holding lock, try next list + // *TODO: For UI/debug-oriented lists, we might drop the fine- + // grained locking as there's a lowered expectation of smoothness + // in these cases. + if (!mDecompositionRequests.empty()) + { + std::set incomplete; + while (!mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) + { + mMutex->lock(); + std::set::iterator iter = mDecompositionRequests.begin(); + UUIDBasedRequest req = *iter; + mDecompositionRequests.erase(iter); + mMutex->unlock(); + if (req.isDelayed()) + { + incomplete.insert(req.mId); + } + else if (!fetchMeshDecomposition(req.mId)) + { + if (req.canRetry()) + { + req.updateTime(); + incomplete.insert(req.mId); + } + else + { + LL_DEBUGS() << "mDecompositionRequests failed: " << req.mId << LL_ENDL; + } + } + } - if (! incomplete.empty()) - { - mDecompositionRequests.insert(incomplete.begin(), incomplete.end()); - } - } + if (!incomplete.empty()) + { + LLMutexLock locker(mMutex); + mDecompositionRequests.insert(incomplete.begin(), incomplete.end()); + } + } - // holding lock, final list - if (! mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) - { - std::set incomplete; - std::set::iterator iter(mPhysicsShapeRequests.begin()); - while (iter != mPhysicsShapeRequests.end() && mHttpRequestSet.size() < sRequestHighWater) - { - LLUUID mesh_id = *iter; - mPhysicsShapeRequests.erase(iter); - mMutex->unlock(); - - if (! fetchMeshPhysicsShape(mesh_id)) - { - incomplete.insert(mesh_id); - } + // holding lock, final list + if (!mPhysicsShapeRequests.empty()) + { + std::set incomplete; + while (!mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) + { + mMutex->lock(); + std::set::iterator iter = mPhysicsShapeRequests.begin(); + UUIDBasedRequest req = *iter; + mPhysicsShapeRequests.erase(iter); + mMutex->unlock(); + if (req.isDelayed()) + { + incomplete.insert(req.mId); + } + else if (!fetchMeshPhysicsShape(req.mId)) + { + if (req.canRetry()) + { + req.updateTime(); + incomplete.insert(req.mId); + } + else + { + LL_DEBUGS() << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL; + } + } + } - mMutex->lock(); - iter = mPhysicsShapeRequests.begin(); - } - - if (! incomplete.empty()) - { - mPhysicsShapeRequests.insert(incomplete.begin(), incomplete.end()); - } - } - mMutex->unlock(); - } + if (!incomplete.empty()) + { + LLMutexLock locker(mMutex); + mPhysicsShapeRequests.insert(incomplete.begin(), incomplete.end()); + } + } + } // For dev purposes only. A dynamic change could make this false // and that shouldn't assert. @@ -1047,19 +1151,19 @@ void LLMeshRepoThread::run() // Mutex: LLMeshRepoThread::mMutex must be held on entry void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id) { - mSkinRequests.insert(mesh_id); + mSkinRequests.insert(UUIDBasedRequest(mesh_id)); } // Mutex: LLMeshRepoThread::mMutex must be held on entry void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id) { - mDecompositionRequests.insert(mesh_id); + mDecompositionRequests.insert(UUIDBasedRequest(mesh_id)); } // Mutex: LLMeshRepoThread::mMutex must be held on entry void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id) { - mPhysicsShapeRequests.insert(mesh_id); + mPhysicsShapeRequests.insert(UUIDBasedRequest(mesh_id)); } void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) @@ -1525,7 +1629,7 @@ void LLMeshRepoThread::decActiveHeaderRequests() } //return false if failed to get header -bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) +bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry) { ++LLMeshRepository::sMeshRequestCount; @@ -1572,7 +1676,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) << LL_ENDL; retval = false; } - else + else if (can_retry) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); @@ -1583,7 +1687,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) } //return false if failed to get mesh lod. -bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry) { if (!mHeaderMutex) { @@ -1616,7 +1720,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) U8* buffer = new(std::nothrow) U8[size]; if (!buffer) { - LL_WARNS_ONCE(LOG_MESH) << "Can't allocate memory for mesh LOD, size: " << size << LL_ENDL; + LL_WARNS_ONCE(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL; // todo: for now it will result in indefinite constant retries, should result in timeout // or in retry-count and disabling mesh. (but usually viewer is beyond saving at this point) return false; @@ -1661,12 +1765,16 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) << LL_ENDL; retval = false; } - else + else if (can_retry) { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); // *NOTE: Allowing a re-request, not marking as unavailable. Is that correct? } + else + { + mUnavailableQ.push(LODRequest(mesh_params, lod)); + } } else { diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index e07a00bf03..22215c784a 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -177,6 +177,21 @@ public: }; +class RequestStats +{ +public: + RequestStats() : mRetries(0) {}; + + void updateTime(); + bool canRetry() const; + bool isDelayed() const; + U32 getRetries() { return mRetries; } + +private: + U32 mRetries; + LLFrameTimer mTimer; +}; + class LLMeshRepoThread : public LLThread { public: @@ -197,14 +212,14 @@ public: mesh_header_map mMeshHeader; std::map mMeshHeaderSize; - - class HeaderRequest + + class HeaderRequest : public RequestStats { public: const LLVolumeParams mMeshParams; HeaderRequest(const LLVolumeParams& mesh_params) - : mMeshParams(mesh_params) + : RequestStats(), mMeshParams(mesh_params) { } @@ -214,7 +229,7 @@ public: } }; - class LODRequest + class LODRequest : public RequestStats { public: LLVolumeParams mMeshParams; @@ -222,7 +237,7 @@ public: F32 mScore; LODRequest(const LLVolumeParams& mesh_params, S32 lod) - : mMeshParams(mesh_params), mLOD(lod), mScore(0.f) + : RequestStats(), mMeshParams(mesh_params), mLOD(lod), mScore(0.f) { } }; @@ -234,7 +249,22 @@ public: return lhs.mScore > rhs.mScore; // greatest = first } }; - + + class UUIDBasedRequest : public RequestStats + { + public: + LLUUID mId; + + UUIDBasedRequest(const LLUUID& id) + : RequestStats(), mId(id) + { + } + + bool operator<(const UUIDBasedRequest& rhs) const + { + return mId < rhs.mId; + } + }; class LoadedMesh { @@ -251,16 +281,16 @@ public: }; //set of requested skin info - std::set mSkinRequests; + std::set mSkinRequests; // list of completed skin info requests std::list mSkinInfoQ; //set of requested decompositions - std::set mDecompositionRequests; + std::set mDecompositionRequests; //set of requested physics shapes - std::set mPhysicsShapeRequests; + std::set mPhysicsShapeRequests; // list of completed Decomposition info requests std::list mDecompositionQ; @@ -304,8 +334,8 @@ public: void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); - bool fetchMeshHeader(const LLVolumeParams& mesh_params); - bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + bool fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry = true); + bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry = true); bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); EMeshProcessingResult lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size); bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); From 14ed34f26dbc2c62fc13984cedf20d8e48c34417 Mon Sep 17 00:00:00 2001 From: andreykproductengine Date: Fri, 4 May 2018 10:35:50 +0300 Subject: [PATCH 12/20] MAINT-7626 Incorporate transfer changes into viewer --- indra/llmessage/llxfermanager.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp index 8f18004791..70def36d51 100644 --- a/indra/llmessage/llxfermanager.cpp +++ b/indra/llmessage/llxfermanager.cpp @@ -1099,7 +1099,6 @@ void LLXferManager::retransmitUnackedPackets() { LL_INFOS() << "resending xfer " << xferp->mRemoteHost << ":" << xferp->getFileName() << " packet unconfirmed after: "<< et << " sec, packet " << xferp->mPacketNum << LL_ENDL; xferp->resendLastPacket(); - xferp = xferp->mNext; } } else if ((xferp->mStatus == e_LL_XFER_REGISTERED) && ( (et = xferp->ACKTimer.getElapsedTimeF32()) > LL_XFER_REGISTRATION_TIMEOUT)) @@ -1137,14 +1136,10 @@ void LLXferManager::retransmitUnackedPackets() xferp->sendNextPacket(); changeNumActiveXfers(xferp->mRemoteHost,1); } - } - xferp = xferp->mNext; + } } - else - { - xferp = xferp->mNext; - } - } + ++iter; + } // end while() loop // // HACK - if we're using xfer confirm throttling, throttle our xfer confirms here @@ -1240,7 +1235,6 @@ void LLXferManager::startPendingDownloads() { ++download_count; } - xferp = xferp->mNext; } S32 start_count = mMaxIncomingXfers - download_count; From e31d876c6c0a3443ba144f122d3a17c565df0cdc Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Fri, 4 May 2018 15:50:31 +0300 Subject: [PATCH 13/20] MAINT-8616 FIXED Edit Tool often displays the wrong Creator and/or Owner of an object --- indra/newview/llpanelpermissions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index 0e5a7aec0d..be34b6d8cd 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -399,6 +399,7 @@ void LLPanelPermissions::refresh() style_params.font.style = "UNDERLINE"; LLAvatarName av_name; + style_params.link_href = creator_app_link; if (LLAvatarNameCache::get(mCreatorID, &av_name)) { updateCreatorName(mCreatorID, av_name, style_params); From 815067ae29d9cf8dc8ce4334bbac0c9aec6e7914 Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Fri, 4 May 2018 18:19:50 +0300 Subject: [PATCH 14/20] MAINT-8622 FIXED "Share" checkbox becomes gray after sharing the object (no modify) for the group --- indra/newview/llpanelpermissions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index be34b6d8cd..cb46ce8c42 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -810,7 +810,7 @@ void LLPanelPermissions::refresh() else { getChild("checkbox share with group")->setValue(TRUE); - getChild("checkbox share with group")->setTentative( TRUE); + getChild("checkbox share with group")->setTentative(!has_change_perm_ability); getChildView("button deed")->setEnabled(gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && (group_mask_on & PERM_MOVE) && (owner_mask_on & PERM_TRANSFER) && !group_owned && can_transfer); } } From 0abe3a4919bef4f80ccca23eef85fcf195165b37 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 4 May 2018 15:46:05 +0000 Subject: [PATCH 15/20] MAINT-8593 Viewer should not repeat mesh dowloads indefinetely --- indra/newview/llmeshrepository.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 4011328104..bb14a29ca3 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -982,7 +982,7 @@ void LLMeshRepoThread::run() // failed to load before, wait a bit incomplete.push_front(req); } - else if (!fetchMeshHeader(req.mMeshParams, req.getRetries())) + else if (!fetchMeshHeader(req.mMeshParams, req.canRetry())) { if (req.canRetry()) { @@ -1071,14 +1071,14 @@ void LLMeshRepoThread::run() mMutex->unlock(); if (req.isDelayed()) { - incomplete.insert(req.mId); + incomplete.insert(req); } else if (!fetchMeshDecomposition(req.mId)) { if (req.canRetry()) { req.updateTime(); - incomplete.insert(req.mId); + incomplete.insert(req); } else { @@ -1097,7 +1097,7 @@ void LLMeshRepoThread::run() // holding lock, final list if (!mPhysicsShapeRequests.empty()) { - std::set incomplete; + std::set incomplete; while (!mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) { mMutex->lock(); @@ -1107,14 +1107,14 @@ void LLMeshRepoThread::run() mMutex->unlock(); if (req.isDelayed()) { - incomplete.insert(req.mId); + incomplete.insert(req); } else if (!fetchMeshPhysicsShape(req.mId)) { if (req.canRetry()) { req.updateTime(); - incomplete.insert(req.mId); + incomplete.insert(req); } else { From 8fe1a35424432f4331a9038ef2fee2d2602dfdad Mon Sep 17 00:00:00 2001 From: maxim_productengine Date: Mon, 7 May 2018 13:42:42 +0300 Subject: [PATCH 16/20] MAINT-8628 FIXED Group notice edit field is missing inventory icon --- .../skins/default/xui/en/panel_group_notices.xml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/panel_group_notices.xml b/indra/newview/skins/default/xui/en/panel_group_notices.xml index c8ce5cdebf..47aceb2c2e 100644 --- a/indra/newview/skins/default/xui/en/panel_group_notices.xml +++ b/indra/newview/skins/default/xui/en/panel_group_notices.xml @@ -180,13 +180,20 @@ Maximum 200 per group daily + +