summaryrefslogtreecommitdiff
path: root/indra/newview/llviewerobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llviewerobject.cpp')
-rw-r--r--indra/newview/llviewerobject.cpp160
1 files changed, 149 insertions, 11 deletions
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 9ad2474e9b..8ea5ee33f3 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -2915,24 +2915,33 @@ void LLViewerObject::fetchInventoryFromServer()
delete mInventory;
mInventory = NULL;
- // Results in processTaskInv
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_RequestTaskInventory);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_InventoryData);
- msg->addU32Fast(_PREHASH_LocalID, mLocalID);
- msg->sendReliable(mRegionp->getHost());
-
// This will get reset by doInventoryCallback or processTaskInv
mInvRequestState = INVENTORY_REQUEST_PENDING;
+
+ if (mRegionp && !mRegionp->getCapability("RequestTaskInventory").empty())
+ {
+ LLCoros::instance().launch("LLViewerObject::fetchInventoryFromCapCoro()",
+ boost::bind(&LLViewerObject::fetchInventoryFromCapCoro, mID));
+ }
+ else
+ {
+ LL_WARNS() << "Using old task inventory path!" << LL_ENDL;
+ // Results in processTaskInv
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RequestTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->sendReliable(mRegionp->getHost());
+ }
}
}
void LLViewerObject::fetchInventoryDelayed(const F64 &time_seconds)
{
- // unless already waiting, drop previous request and shedule an update
+ // unless already waiting, drop previous request and schedule an update
if (mInvRequestState != INVENTORY_REQUEST_WAIT)
{
if (mInvRequestXFerId != 0)
@@ -2963,6 +2972,80 @@ void LLViewerObject::fetchInventoryDelayedCoro(const LLUUID task_inv, const F64
}
}
+//static
+void LLViewerObject::fetchInventoryFromCapCoro(const LLUUID task_inv)
+{
+ LLViewerObject *obj = gObjectList.findObject(task_inv);
+ if (obj)
+ {
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TaskInventoryRequest", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ std::string url = obj->mRegionp->getCapability("RequestTaskInventory") + "?task_id=" + obj->mID.asString();
+ // If we already have a copy of the inventory then add it so the server won't re-send something we already have.
+ // We expect this case to crop up in the case of failed inventory mutations, but it might happen otherwise as well.
+ if (obj->mInventorySerialNum && obj->mInventory)
+ url += "&inventory_serial=" + std::to_string(obj->mInventorySerialNum);
+
+ obj->mInvRequestState = INVENTORY_XFER;
+ LLSD result = httpAdapter->getAndSuspend(httpRequest, url);
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ // Object may have gone away while we were suspended, double-check that it still exists
+ obj = gObjectList.findObject(task_inv);
+ if (!obj)
+ {
+ LL_WARNS() << "Object " << task_inv << " went away while fetching inventory, dropping result" << LL_ENDL;
+ return;
+ }
+
+ bool potentially_stale = false;
+ if (status)
+ {
+ // Dealing with inventory serials is kind of funky. They're monotonically increasing and 16 bits,
+ // so we expect them to overflow, but we can use inv serial < expected serial as a signal that we may
+ // have mutated the task inventory since we kicked off the request, and those mutations may have not
+ // been taken into account yet. Of course, those mutations may have actually failed which would result
+ // in the inv serial never increasing.
+ //
+ // When we detect this case, set the expected inv serial to the inventory serial we actually received
+ // and kick off a re-request after a slight delay.
+ S16 serial = (S16)result["inventory_serial"].asInteger();
+ potentially_stale = serial < obj->mExpectedInventorySerialNum;
+ LL_INFOS() << "Inventory loaded for " << task_inv << LL_ENDL;
+ obj->mInventorySerialNum = serial;
+ obj->mExpectedInventorySerialNum = serial;
+ obj->loadTaskInvLLSD(result);
+ }
+ else if (status.getType() == 304)
+ {
+ LL_INFOS() << "Inventory wasn't changed on server!" << LL_ENDL;
+ obj->mInvRequestState = INVENTORY_REQUEST_STOPPED;
+ // Even though it wasn't necessary to send a response, we still may have mutated
+ // the inventory since we kicked off the request, check for that case.
+ potentially_stale = obj->mInventorySerialNum < obj->mExpectedInventorySerialNum;
+ // Set this to what we already have so that we don't re-request a second time.
+ obj->mExpectedInventorySerialNum = obj->mInventorySerialNum;
+ }
+ else
+ {
+ // Not sure that there's anything sensible we can do to recover here, retrying in a loop would be bad.
+ LL_WARNS() << "Error status while requesting task inventory: " << status.toString() << LL_ENDL;
+ obj->mInvRequestState = INVENTORY_REQUEST_STOPPED;
+ }
+
+ if (potentially_stale)
+ {
+ // Stale? I guess we can use what we got for now, but we'll have to re-request
+ LL_WARNS() << "Stale inv_serial? Re-requesting." << LL_ENDL;
+ obj->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_OUTDATED);
+ }
+ }
+}
+
LLControlAvatar *LLViewerObject::getControlAvatar()
{
return getRootEdit()->mControlAvatar.get();
@@ -3138,6 +3221,20 @@ void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data)
S16 serial = 0;
msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, serial);
+ if (object->mRegionp && !object->mRegionp->getCapability("RequestTaskInventory").empty())
+ {
+ // It seems that simulator may ask us to re-download the task inventory if an update to the inventory
+ // happened out-of-band while we had the object selected (like if a script is saved.)
+ //
+ // If we're meant to use the HTTP capability, ignore the contents of the UDP message and fetch the
+ // inventory via the CAP so that we don't flow down the UDP inventory request path unconditionally here.
+ // We shouldn't need to wait, as any updates should already be ready to fetch by this point.
+ LL_INFOS() << "Handling unsolicited ReplyTaskInventory for " << task_id << LL_ENDL;
+ object->mExpectedInventorySerialNum = serial;
+ object->fetchInventoryFromServer();
+ return;
+ }
+
if (serial == object->mInventorySerialNum
&& serial < object->mExpectedInventorySerialNum)
{
@@ -3345,6 +3442,47 @@ bool LLViewerObject::loadTaskInvFile(const std::string& filename)
return true;
}
+void LLViewerObject::loadTaskInvLLSD(const LLSD& inv_result)
+{
+ if (inv_result.has("contents"))
+ {
+ if(mInventory)
+ {
+ mInventory->clear(); // will deref and delete it
+ }
+ else
+ {
+ mInventory = new LLInventoryObject::object_list_t;
+ }
+
+ // Synthesize the "Contents" category, the viewer expects it, but it isn't sent.
+ LLPointer<LLInventoryObject> inv = new LLInventoryObject(mID, LLUUID::null, LLAssetType::AT_CATEGORY, "Contents");
+ mInventory->push_front(inv);
+
+ const LLSD& inventory = inv_result["contents"];
+ for (const auto& inv_entry : llsd::inArray(inventory))
+ {
+ if (inv_entry.has("item_id"))
+ {
+ LLPointer<LLViewerInventoryItem> inv = new LLViewerInventoryItem;
+ inv->unpackMessage(inv_entry);
+ mInventory->push_front(inv);
+ }
+ else
+ {
+ LL_WARNS_ONCE() << "Unknown inventory entry while reading from inventory file. Entry: '"
+ << inv_entry << "'" << LL_ENDL;
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS() << "unable to load task inventory: " << inv_result << LL_ENDL;
+ return;
+ }
+ doInventoryCallback();
+}
+
void LLViewerObject::doInventoryCallback()
{
for (callback_list_t::iterator iter = mInventoryCallbacks.begin();