summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSalad Dais <SaladDais@users.noreply.github.com>2024-05-30 16:29:58 +0000
committerAndrey Lihatskiy <alihatskiy@productengine.com>2025-03-11 04:21:01 +0200
commit6fd4f13c477705a71c3ad24955872d734a6c3dd6 (patch)
tree14f0bff1d7892a71f5b74a04e68bc99c0d367502
parent4f67df346b8422f837d6e5ac0702b4f664bc26f6 (diff)
Add support for RequestTaskInventory capability
The viewer now prefers to load inventory via the capability rather than over the deprecated Xfer system, though both are still supported. # Conflicts: # indra/newview/llviewerobject.h
-rw-r--r--indra/newview/llviewerobject.cpp146
-rw-r--r--indra/newview/llviewerobject.h2
-rwxr-xr-xindra/newview/llviewerregion.cpp1
3 files changed, 145 insertions, 4 deletions
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 86440fca48..2eeb0640cc 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -2917,6 +2917,17 @@ void LLViewerObject::fetchInventoryFromServer()
delete mInventory;
mInventory = NULL;
+ // 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);
@@ -2926,15 +2937,13 @@ void LLViewerObject::fetchInventoryFromServer()
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;
+ }
}
}
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)
@@ -2965,6 +2974,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();
@@ -3140,6 +3223,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)
{
@@ -3347,6 +3444,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();
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 119b07b1f5..63458e60ea 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -688,6 +688,7 @@ private:
// forms task inventory request after some time passed, marks request as pending
void fetchInventoryDelayed(const F64 &time_seconds);
static void fetchInventoryDelayedCoro(const LLUUID task_inv, const F64 time_seconds);
+ static void fetchInventoryFromCapCoro(const LLUUID task_inv);
public:
//
@@ -826,6 +827,7 @@ protected:
static void processTaskInvFile(void** user_data, S32 error_code, LLExtStat ext_status);
bool loadTaskInvFile(const std::string& filename);
+ void loadTaskInvLLSD(const LLSD &inv_result);
void doInventoryCallback();
bool isOnMap();
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index c21aeb1d57..697433148b 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -3239,6 +3239,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("FetchInventory2");
capabilityNames.append("FetchInventoryDescendents2");
capabilityNames.append("IncrementCOFVersion");
+ capabilityNames.append("RequestTaskInventory");
AISAPI::getCapNames(capabilityNames);
capabilityNames.append("InterestList");