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
master
Salad Dais 2024-05-30 16:29:58 +00:00 committed by Andrey Lihatskiy
parent 4f67df346b
commit 6fd4f13c47
3 changed files with 145 additions and 4 deletions

View File

@ -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();

View File

@ -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();

View File

@ -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");