diff options
Diffstat (limited to 'indra/newview/llcompilequeue.cpp')
-rw-r--r-- | indra/newview/llcompilequeue.cpp | 1652 |
1 files changed, 826 insertions, 826 deletions
diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index 906c61c535..552ea75559 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -1,826 +1,826 @@ -/**
- * @file llcompilequeue.cpp
- * @brief LLCompileQueueData class implementation
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-/**
- *
- * Implementation of the script queue which keeps an array of object
- * UUIDs and manipulates all of the scripts on each of them.
- *
- */
-
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llcompilequeue.h"
-
-#include "llagent.h"
-#include "llchat.h"
-#include "llfloaterreg.h"
-#include "llviewerwindow.h"
-#include "llviewerobject.h"
-#include "llviewerobjectlist.h"
-#include "llviewerregion.h"
-#include "llviewercontrol.h"
-#include "llviewerobject.h"
-#include "llviewerregion.h"
-#include "llresmgr.h"
-
-#include "llbutton.h"
-#include "lldir.h"
-#include "llnotificationsutil.h"
-#include "llviewerstats.h"
-#include "llfilesystem.h"
-#include "lluictrlfactory.h"
-#include "lltrans.h"
-
-#include "llselectmgr.h"
-#include "llexperiencecache.h"
-
-#include "llviewerassetupload.h"
-#include "llcorehttputil.h"
-
-namespace
-{
-
- const std::string QUEUE_EVENTPUMP_NAME("ScriptActionQueue");
- const F32 QUEUE_INVENTORY_FETCH_TIMEOUT = 300.f;
-
- // ObjectIventoryFetcher is an adapter between the LLVOInventoryListener::inventoryChanged
- // callback mechanism and the LLEventPump coroutine architecture allowing the
- // coroutine to wait for the inventory event.
- class ObjectInventoryFetcher: public LLVOInventoryListener
- {
- public:
- typedef std::shared_ptr<ObjectInventoryFetcher> ptr_t;
-
- ObjectInventoryFetcher(LLEventPump &pump, LLViewerObject* object, void* user_data) :
- mPump(pump),
- LLVOInventoryListener()
- {
- registerVOInventoryListener(object, this);
- }
-
- virtual void inventoryChanged(LLViewerObject* object,
- LLInventoryObject::object_list_t* inventory,
- S32 serial_num,
- void* user_data);
-
- void fetchInventory()
- {
- requestVOInventory();
- }
-
- const LLInventoryObject::object_list_t & getInventoryList() const { return mInventoryList; }
-
- private:
- LLInventoryObject::object_list_t mInventoryList;
- LLEventPump & mPump;
- };
-
- class HandleScriptUserData
- {
- public:
- HandleScriptUserData(const std::string &pumpname) :
- mPumpname(pumpname)
- { }
-
- const std::string &getPumpName() const { return mPumpname; }
-
- private:
- std::string mPumpname;
- };
-
-
-}
-
-// *NOTE$: A minor specialization of LLScriptAssetUpload, it does not require a buffer
-// (and does not save a buffer to the cache) and it finds the compile queue window and
-// displays a compiling message.
-class LLQueuedScriptAssetUpload : public LLScriptAssetUpload
-{
-public:
- LLQueuedScriptAssetUpload(LLUUID taskId, LLUUID itemId, LLUUID assetId, TargetType_t targetType,
- bool isRunning, std::string scriptName, LLUUID queueId, LLUUID exerienceId, taskUploadFinish_f finish) :
- LLScriptAssetUpload(taskId, itemId, targetType, isRunning,
- exerienceId, std::string(), finish, nullptr),
- mScriptName(scriptName),
- mQueueId(queueId)
- {
- setAssetId(assetId);
- }
-
- virtual LLSD prepareUpload()
- {
- /* *NOTE$: The parent class (LLScriptAssetUpload will attempt to save
- * the script buffer into to the cache. Since the resource is already in
- * the cache we don't want to do that. Just put a compiling message in
- * the window and move on
- */
- LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", LLSD(mQueueId));
- if (queue)
- {
- std::string message = std::string("Compiling \"") + getScriptName() + std::string("\"...");
-
- queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM);
- }
-
- return LLSDMap("success", LLSD::Boolean(true));
- }
-
-
- std::string getScriptName() const { return mScriptName; }
-
-private:
- void setScriptName(const std::string &scriptName) { mScriptName = scriptName; }
-
- LLUUID mQueueId;
- std::string mScriptName;
-};
-
-///----------------------------------------------------------------------------
-/// Local function declarations, constants, enums, and typedefs
-///----------------------------------------------------------------------------
-
-struct LLScriptQueueData
-{
- LLUUID mQueueID;
- LLUUID mTaskId;
- LLPointer<LLInventoryItem> mItem;
- LLHost mHost;
- LLUUID mExperienceId;
- std::string mExperiencename;
- LLScriptQueueData(const LLUUID& q_id, const LLUUID& task_id, LLInventoryItem* item) :
- mQueueID(q_id), mTaskId(task_id), mItem(new LLInventoryItem(item)) {}
-
-};
-
-///----------------------------------------------------------------------------
-/// Class LLFloaterScriptQueue
-///----------------------------------------------------------------------------
-
-// Default constructor
-LLFloaterScriptQueue::LLFloaterScriptQueue(const LLSD& key) :
- LLFloater(key),
- mDone(false),
- mMono(false)
-{
-
-}
-
-// Destroys the object
-LLFloaterScriptQueue::~LLFloaterScriptQueue()
-{
-}
-
-bool LLFloaterScriptQueue::postBuild()
-{
- childSetAction("close",onCloseBtn,this);
- getChildView("close")->setEnabled(false);
- setVisible(true);
- return true;
-}
-
-// static
-void LLFloaterScriptQueue::onCloseBtn(void* user_data)
-{
- LLFloaterScriptQueue* self = (LLFloaterScriptQueue*)user_data;
- self->closeFloater();
-}
-
-void LLFloaterScriptQueue::addObject(const LLUUID& id, std::string name)
-{
- ObjectData obj = { id, name };
- mObjectList.push_back(obj);
-}
-
-bool LLFloaterScriptQueue::start()
-{
- std::string buffer;
-
- LLStringUtil::format_map_t args;
- args["[START]"] = mStartString;
- args["[COUNT]"] = llformat ("%d", mObjectList.size());
- buffer = getString ("Starting", args);
-
- getChild<LLScrollListCtrl>("queue output")->addSimpleElement(buffer, ADD_BOTTOM);
-
- return startQueue();
-}
-
-void LLFloaterScriptQueue::addProcessingMessage(const std::string &message, const LLSD &args)
-{
- std::string buffer(LLTrans::getString(message, args));
-
- getChild<LLScrollListCtrl>("queue output")->addSimpleElement(buffer, ADD_BOTTOM);
-}
-
-void LLFloaterScriptQueue::addStringMessage(const std::string &message)
-{
- getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM);
-}
-
-
-bool LLFloaterScriptQueue::isDone() const
-{
- return (mCurrentObjectID.isNull() && (mObjectList.size() == 0));
-}
-
-///----------------------------------------------------------------------------
-/// Class LLFloaterCompileQueue
-///----------------------------------------------------------------------------
-LLFloaterCompileQueue::LLFloaterCompileQueue(const LLSD& key)
- : LLFloaterScriptQueue(key)
-{
- setTitle(LLTrans::getString("CompileQueueTitle"));
- setStartString(LLTrans::getString("CompileQueueStart"));
-
-}
-
-LLFloaterCompileQueue::~LLFloaterCompileQueue()
-{
-}
-
-void LLFloaterCompileQueue::experienceIdsReceived( const LLSD& content )
-{
- for(LLSD::array_const_iterator it = content.beginArray(); it != content.endArray(); ++it)
- {
- mExperienceIds.insert(it->asUUID());
- }
-}
-
-bool LLFloaterCompileQueue::hasExperience( const LLUUID& id ) const
-{
- return mExperienceIds.find(id) != mExperienceIds.end();
-}
-
-// //Attempt to record this asset ID. If it can not be inserted into the set
-// //then it has already been processed so return false.
-
-void LLFloaterCompileQueue::handleHTTPResponse(std::string pumpName, const LLSD &expresult)
-{
- LLEventPumps::instance().post(pumpName, expresult);
-}
-
-// *TODO: handleSCriptRetrieval is passed into the cache via a legacy C function pointer
-// future project would be to convert these to C++ callables (std::function<>) so that
-// we can use bind and remove the userData parameter.
-//
-void LLFloaterCompileQueue::handleScriptRetrieval(const LLUUID& assetId,
- LLAssetType::EType type, void* userData, S32 status, LLExtStat extStatus)
-{
- LLSD result(LLSD::emptyMap());
-
- result["asset_id"] = assetId;
- if (status)
- {
- result["error"] = status;
-
- if (status == LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE)
- {
- result["message"] = LLTrans::getString("CompileQueueProblemDownloading") + (":");
- result["alert"] = LLTrans::getString("CompileQueueScriptNotFound");
- }
- else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
- {
- result["message"] = LLTrans::getString("CompileQueueInsufficientPermFor") + (":");
- result["alert"] = LLTrans::getString("CompileQueueInsufficientPermDownload");
- }
- else
- {
- result["message"] = LLTrans::getString("CompileQueueUnknownFailure");
- }
- }
-
- LLEventPumps::instance().post(((HandleScriptUserData *)userData)->getPumpName(), result);
-
-}
-
-/*static*/
-void LLFloaterCompileQueue::processExperienceIdResults(LLSD result, LLUUID parent)
-{
- LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", parent);
- if (!queue)
- return;
-
- queue->experienceIdsReceived(result["experience_ids"]);
-
- // getDerived handle gets a handle that can be resolved to a parent class of the derived object.
- LLHandle<LLFloaterScriptQueue> hFloater(queue->getDerivedHandle<LLFloaterScriptQueue>());
-
- // note subtle difference here: getDerivedHandle in this case is for an LLFloaterCompileQueue
- fnQueueAction_t fn = boost::bind(LLFloaterCompileQueue::processScript,
- queue->getDerivedHandle<LLFloaterCompileQueue>(), _1, _2, _3);
-
-
- LLCoros::instance().launch("ScriptQueueCompile", boost::bind(LLFloaterScriptQueue::objectScriptProcessingQueueCoro,
- queue->mStartString,
- hFloater,
- queue->mObjectList,
- fn));
-
-}
-
-/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro.
-/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception.
-bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloater,
- const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump)
-{
- if (LLApp::isExiting())
- {
- // Reply from coroutine came on shutdown
- // We are quiting, don't start any more coroutines!
- return true;
- }
-
- LLSD result;
- LLCheckedHandle<LLFloaterCompileQueue> floater(hfloater);
- // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle.
- // which is caught in objectScriptProcessingQueueCoro
- bool monocompile = floater->mMono;
-
- // Initial test to see if we can (or should) attempt to compile the script.
- LLInventoryItem *item = dynamic_cast<LLInventoryItem *>(inventory);
-
- if (!item)
- {
- LL_WARNS("SCRIPTQ") << "item retrieved is not an LLInventoryItem." << LL_ENDL;
- return true;
- }
-
- if (!item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID()) ||
- !item->getPermissions().allowCopyBy(gAgent.getID(), gAgent.getGroupID()))
- {
- std::string buffer = "Skipping: " + item->getName() + "(Permissions)";
- floater->addStringMessage(buffer);
- return true;
- }
-
- // Attempt to retrieve the experience
- LLUUID experienceId;
- {
- LLExperienceCache::instance().fetchAssociatedExperience(inventory->getParentUUID(), inventory->getUUID(),
- boost::bind(&LLFloaterCompileQueue::handleHTTPResponse, pump.getName(), _1));
-
- result = llcoro::suspendUntilEventOnWithTimeout(pump, QUEUE_INVENTORY_FETCH_TIMEOUT,
- LLSDMap("timeout", LLSD::Boolean(true)));
-
- floater.check();
-
- if (result.has("timeout"))
- { // A timeout filed in the result will always be true if present.
- LLStringUtil::format_map_t args;
- args["[OBJECT_NAME]"] = inventory->getName();
- std::string buffer = floater->getString("Timeout", args);
- floater->addStringMessage(buffer);
- return true;
- }
-
- if (result.has(LLExperienceCache::EXPERIENCE_ID))
- {
- experienceId = result[LLExperienceCache::EXPERIENCE_ID].asUUID();
- if (!floater->hasExperience(experienceId))
- {
- floater->addProcessingMessage("CompileNoExperiencePerm",
- LLSDMap("SCRIPT", inventory->getName())
- ("EXPERIENCE", result[LLExperienceCache::NAME].asString()));
- return true;
- }
- }
-
- }
-
- if (!gAssetStorage)
- {
- // viewer likely is shutting down
- return true;
- }
-
- {
- HandleScriptUserData userData(pump.getName());
-
-
- // request the asset
- gAssetStorage->getInvItemAsset(LLHost(),
- gAgent.getID(),
- gAgent.getSessionID(),
- item->getPermissions().getOwner(),
- object->getID(),
- item->getUUID(),
- item->getAssetUUID(),
- item->getType(),
- &LLFloaterCompileQueue::handleScriptRetrieval,
- &userData);
-
- result = llcoro::suspendUntilEventOnWithTimeout(pump, QUEUE_INVENTORY_FETCH_TIMEOUT,
- LLSDMap("timeout", LLSD::Boolean(true)));
- }
-
- if (result.has("timeout"))
- { // A timeout filed in the result will always be true if present.
- LLStringUtil::format_map_t args;
- args["[OBJECT_NAME]"] = inventory->getName();
- std::string buffer = floater->getString("Timeout", args);
- floater->addStringMessage(buffer);
- return true;
- }
-
- if (result.has("error"))
- {
- LL_WARNS("SCRIPTQ") << "Inventory fetch returned with error. Code: " << result["error"].asString() << LL_ENDL;
- std::string buffer = result["message"].asString() + " " + inventory->getName();
- floater->addStringMessage(buffer);
-
- if (result.has("alert"))
- {
- LLSD args;
- args["MESSAGE"] = result["alert"].asString();
- LLNotificationsUtil::add("SystemMessage", args);
- }
- return true;
- }
-
- LLUUID assetId = result["asset_id"];
-
- std::string url = object->getRegion()->getCapability("UpdateScriptTask");
-
- {
- LLResourceUploadInfo::ptr_t uploadInfo(new LLQueuedScriptAssetUpload(object->getID(),
- inventory->getUUID(),
- assetId,
- monocompile ? LLScriptAssetUpload::MONO : LLScriptAssetUpload::LSL2,
- true,
- inventory->getName(),
- LLUUID(),
- experienceId,
- boost::bind(&LLFloaterCompileQueue::handleHTTPResponse, pump.getName(), _4)));
-
- LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo);
- }
-
- result = llcoro::suspendUntilEventOnWithTimeout(pump, QUEUE_INVENTORY_FETCH_TIMEOUT, LLSDMap("timeout", LLSD::Boolean(true)));
-
- floater.check();
-
- if (result.has("timeout"))
- { // A timeout filed in the result will always be true if present.
- LLStringUtil::format_map_t args;
- args["[OBJECT_NAME]"] = inventory->getName();
- std::string buffer = floater->getString("Timeout", args);
- floater->addStringMessage(buffer);
- return true;
- }
-
- // Bytecode save completed
- if (result["compiled"])
- {
- std::string buffer = std::string("Compilation of \"") + inventory->getName() + std::string("\" succeeded");
-
- floater->addStringMessage(buffer);
- LL_INFOS() << buffer << LL_ENDL;
- }
- else
- {
- LLSD compile_errors = result["errors"];
- std::string buffer = std::string("Compilation of \"") + inventory->getName() + std::string("\" failed:");
- floater->addStringMessage(buffer);
- for (LLSD::array_const_iterator line = compile_errors.beginArray();
- line < compile_errors.endArray(); line++)
- {
- std::string str = line->asString();
- str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
-
- floater->addStringMessage(str);
- }
- LL_INFOS() << result["errors"] << LL_ENDL;
- }
-
- return true;
-}
-
-bool LLFloaterCompileQueue::startQueue()
-{
- LLViewerRegion* region = gAgent.getRegion();
- if (region)
- {
- std::string lookup_url = region->getCapability("GetCreatorExperiences");
- if (!lookup_url.empty())
- {
- LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t success =
- boost::bind(&LLFloaterCompileQueue::processExperienceIdResults, _1, getKey().asUUID());
-
- LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t failure =
- boost::bind(&LLFloaterCompileQueue::processExperienceIdResults, LLSD(), getKey().asUUID());
-
- LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(lookup_url,
- success, failure);
- return true;
- }
- }
-
- return true;
-}
-
-
-///----------------------------------------------------------------------------
-/// Class LLFloaterResetQueue
-///----------------------------------------------------------------------------
-
-LLFloaterResetQueue::LLFloaterResetQueue(const LLSD& key)
- : LLFloaterScriptQueue(key)
-{
- setTitle(LLTrans::getString("ResetQueueTitle"));
- setStartString(LLTrans::getString("ResetQueueStart"));
-}
-
-LLFloaterResetQueue::~LLFloaterResetQueue()
-{
-}
-
-/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro.
-/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception.
-bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater,
- const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump)
-{
- LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater);
- // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle.
- // which is caught in objectScriptProcessingQueueCoro
-
- std::string buffer;
- buffer = floater->getString("Resetting") + (": ") + inventory->getName();
- floater->addStringMessage(buffer);
-
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_ScriptReset);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_Script);
- msg->addUUIDFast(_PREHASH_ObjectID, object->getID());
- msg->addUUIDFast(_PREHASH_ItemID, inventory->getUUID());
- msg->sendReliable(object->getRegion()->getHost());
-
- return true;
-}
-
-bool LLFloaterResetQueue::startQueue()
-{
- // Bind the resetObjectScripts method into a QueueAction function and pass it
- // into the object queue processing coroutine.
- fnQueueAction_t fn = boost::bind(LLFloaterResetQueue::resetObjectScripts,
- getDerivedHandle<LLFloaterScriptQueue>(), _1, _2, _3);
-
- LLCoros::instance().launch("ScriptResetQueue", boost::bind(LLFloaterScriptQueue::objectScriptProcessingQueueCoro,
- mStartString,
- getDerivedHandle<LLFloaterScriptQueue>(),
- mObjectList,
- fn));
-
- return true;
-}
-
-///----------------------------------------------------------------------------
-/// Class LLFloaterRunQueue
-///----------------------------------------------------------------------------
-
-LLFloaterRunQueue::LLFloaterRunQueue(const LLSD& key)
- : LLFloaterScriptQueue(key)
-{
- setTitle(LLTrans::getString("RunQueueTitle"));
- setStartString(LLTrans::getString("RunQueueStart"));
-}
-
-LLFloaterRunQueue::~LLFloaterRunQueue()
-{
-}
-
-/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro.
-/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception.
-bool LLFloaterRunQueue::runObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater,
- const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump)
-{
- LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater);
- // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle.
- // which is caught in objectScriptProcessingQueueCoro
-
- std::string buffer;
- buffer = floater->getString("Running") + (": ") + inventory->getName();
- floater->addStringMessage(buffer);
-
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_SetScriptRunning);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_Script);
- msg->addUUIDFast(_PREHASH_ObjectID, object->getID());
- msg->addUUIDFast(_PREHASH_ItemID, inventory->getUUID());
- msg->addBOOLFast(_PREHASH_Running, true);
- msg->sendReliable(object->getRegion()->getHost());
-
- return true;
-}
-
-bool LLFloaterRunQueue::startQueue()
-{
- LLHandle<LLFloaterScriptQueue> hFloater(getDerivedHandle<LLFloaterScriptQueue>());
- fnQueueAction_t fn = boost::bind(LLFloaterRunQueue::runObjectScripts, hFloater, _1, _2, _3);
-
- LLCoros::instance().launch("ScriptRunQueue", boost::bind(LLFloaterScriptQueue::objectScriptProcessingQueueCoro,
- mStartString,
- hFloater,
- mObjectList,
- fn));
-
- return true;
-}
-
-
-///----------------------------------------------------------------------------
-/// Class LLFloaterNotRunQueue
-///----------------------------------------------------------------------------
-
-LLFloaterNotRunQueue::LLFloaterNotRunQueue(const LLSD& key)
- : LLFloaterScriptQueue(key)
-{
- setTitle(LLTrans::getString("NotRunQueueTitle"));
- setStartString(LLTrans::getString("NotRunQueueStart"));
-}
-
-LLFloaterNotRunQueue::~LLFloaterNotRunQueue()
-{
-}
-
-/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro.
-/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception.
-bool LLFloaterNotRunQueue::stopObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater,
- const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump)
-{
- LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater);
- // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle.
- // which is caught in objectScriptProcessingQueueCoro
-
- std::string buffer;
- buffer = floater->getString("NotRunning") + (": ") + inventory->getName();
- floater->addStringMessage(buffer);
-
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_SetScriptRunning);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_Script);
- msg->addUUIDFast(_PREHASH_ObjectID, object->getID());
- msg->addUUIDFast(_PREHASH_ItemID, inventory->getUUID());
- msg->addBOOLFast(_PREHASH_Running, false);
- msg->sendReliable(object->getRegion()->getHost());
-
- return true;
-}
-
-bool LLFloaterNotRunQueue::startQueue()
-{
- LLHandle<LLFloaterScriptQueue> hFloater(getDerivedHandle<LLFloaterScriptQueue>());
-
- fnQueueAction_t fn = boost::bind(&LLFloaterNotRunQueue::stopObjectScripts, hFloater, _1, _2, _3);
- LLCoros::instance().launch("ScriptQueueNotRun", boost::bind(LLFloaterScriptQueue::objectScriptProcessingQueueCoro,
- mStartString,
- hFloater,
- mObjectList,
- fn));
-
- return true;
-}
-
-///----------------------------------------------------------------------------
-/// Local function definitions
-///----------------------------------------------------------------------------
-void ObjectInventoryFetcher::inventoryChanged(LLViewerObject* object,
- LLInventoryObject::object_list_t* inventory, S32 serial_num, void* user_data)
-{
- mInventoryList.clear();
- mInventoryList.assign(inventory->begin(), inventory->end());
-
- mPump.post(LLSDMap("changed", LLSD::Boolean(true)));
-
-}
-
-void LLFloaterScriptQueue::objectScriptProcessingQueueCoro(std::string action, LLHandle<LLFloaterScriptQueue> hfloater,
- object_data_list_t objectList, fnQueueAction_t func)
-{
- LLCoros::set_consuming(true);
- LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater);
- // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle.
- // This is expected if the dialog closes.
- LLEventMailDrop maildrop(QUEUE_EVENTPUMP_NAME, true);
-
- try
- {
- for (object_data_list_t::iterator itObj(objectList.begin()); (itObj != objectList.end()); ++itObj)
- {
- bool firstForObject = true;
- LLUUID object_id = (*itObj).mObjectId;
- LL_INFOS("SCRIPTQ") << "Next object in queue with ID=" << object_id.asString() << LL_ENDL;
-
- LLPointer<LLViewerObject> obj = gObjectList.findObject(object_id);
- LLInventoryObject::object_list_t inventory;
- if (obj)
- {
- ObjectInventoryFetcher::ptr_t fetcher(new ObjectInventoryFetcher(maildrop, obj, NULL));
-
- fetcher->fetchInventory();
-
- LLStringUtil::format_map_t args;
- args["[OBJECT_NAME]"] = (*itObj).mObjectName;
- floater->addStringMessage(floater->getString("LoadingObjInv", args));
-
- LLSD result = llcoro::suspendUntilEventOnWithTimeout(maildrop, QUEUE_INVENTORY_FETCH_TIMEOUT,
- LLSDMap("timeout", LLSD::Boolean(true)));
-
- if (result.has("timeout"))
- { // A timeout filed in the result will always be true if present.
- LL_WARNS("SCRIPTQ") << "Unable to retrieve inventory for object " << object_id.asString() <<
- ". Skipping to next object." << LL_ENDL;
-
- LLStringUtil::format_map_t args;
- args["[OBJECT_NAME]"] = (*itObj).mObjectName;
- floater->addStringMessage(floater->getString("Timeout", args));
-
- continue;
- }
-
- inventory.assign(fetcher->getInventoryList().begin(), fetcher->getInventoryList().end());
- }
- else
- {
- LL_WARNS("SCRIPTQ") << "Unable to retrieve object with ID of " << object_id <<
- ". Skipping to next." << LL_ENDL;
- continue;
- }
-
- // TODO: Get the name of the object we are looking at here so that we can display it below.
- //std::string objName = (dynamic_cast<LLInventoryObject *>(obj.get()))->getName();
- LL_DEBUGS("SCRIPTQ") << "Object has " << inventory.size() << " items." << LL_ENDL;
-
- for (LLInventoryObject::object_list_t::iterator itInv = inventory.begin();
- itInv != inventory.end(); ++itInv)
- {
- floater.check();
-
- // note, we have a smart pointer to the obj above... but if we didn't we'd check that
- // it still exists here.
-
- if (((*itInv)->getType() == LLAssetType::AT_LSL_TEXT))
- {
- LL_DEBUGS("SCRIPTQ") << "Inventory item " << (*itInv)->getUUID().asString() << "\"" << (*itInv)->getName() << "\"" << LL_ENDL;
- if (firstForObject)
- {
- //floater->addStringMessage(objName + ":");
- firstForObject = false;
- }
-
- if (!func(obj, (*itInv), maildrop))
- {
- continue;
- }
- }
-
- // no other explicit suspension point in this loop. func(...) MIGHT suspend
- // but offers no guarantee of doing so.
- llcoro::suspend();
- }
- floater.check();
- }
-
- floater->addStringMessage("Done");
- floater->getChildView("close")->setEnabled(true);
- }
- catch (LLCheckedHandleBase::Stale &)
- {
- // This is expected. It means that floater has been closed before
- // processing was completed.
- LL_DEBUGS("SCRIPTQ") << "LLExeceptionStaleHandle caught! Floater has most likely been closed." << LL_ENDL;
- }
-}
+/** + * @file llcompilequeue.cpp + * @brief LLCompileQueueData class implementation + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/** + * + * Implementation of the script queue which keeps an array of object + * UUIDs and manipulates all of the scripts on each of them. + * + */ + + +#include "llviewerprecompiledheaders.h" + +#include "llcompilequeue.h" + +#include "llagent.h" +#include "llchat.h" +#include "llfloaterreg.h" +#include "llviewerwindow.h" +#include "llviewerobject.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" +#include "llviewercontrol.h" +#include "llviewerobject.h" +#include "llviewerregion.h" +#include "llresmgr.h" + +#include "llbutton.h" +#include "lldir.h" +#include "llnotificationsutil.h" +#include "llviewerstats.h" +#include "llfilesystem.h" +#include "lluictrlfactory.h" +#include "lltrans.h" + +#include "llselectmgr.h" +#include "llexperiencecache.h" + +#include "llviewerassetupload.h" +#include "llcorehttputil.h" + +namespace +{ + + const std::string QUEUE_EVENTPUMP_NAME("ScriptActionQueue"); + const F32 QUEUE_INVENTORY_FETCH_TIMEOUT = 300.f; + + // ObjectIventoryFetcher is an adapter between the LLVOInventoryListener::inventoryChanged + // callback mechanism and the LLEventPump coroutine architecture allowing the + // coroutine to wait for the inventory event. + class ObjectInventoryFetcher: public LLVOInventoryListener + { + public: + typedef std::shared_ptr<ObjectInventoryFetcher> ptr_t; + + ObjectInventoryFetcher(LLEventPump &pump, LLViewerObject* object, void* user_data) : + mPump(pump), + LLVOInventoryListener() + { + registerVOInventoryListener(object, this); + } + + virtual void inventoryChanged(LLViewerObject* object, + LLInventoryObject::object_list_t* inventory, + S32 serial_num, + void* user_data); + + void fetchInventory() + { + requestVOInventory(); + } + + const LLInventoryObject::object_list_t & getInventoryList() const { return mInventoryList; } + + private: + LLInventoryObject::object_list_t mInventoryList; + LLEventPump & mPump; + }; + + class HandleScriptUserData + { + public: + HandleScriptUserData(const std::string &pumpname) : + mPumpname(pumpname) + { } + + const std::string &getPumpName() const { return mPumpname; } + + private: + std::string mPumpname; + }; + + +} + +// *NOTE$: A minor specialization of LLScriptAssetUpload, it does not require a buffer +// (and does not save a buffer to the cache) and it finds the compile queue window and +// displays a compiling message. +class LLQueuedScriptAssetUpload : public LLScriptAssetUpload +{ +public: + LLQueuedScriptAssetUpload(LLUUID taskId, LLUUID itemId, LLUUID assetId, TargetType_t targetType, + bool isRunning, std::string scriptName, LLUUID queueId, LLUUID exerienceId, taskUploadFinish_f finish) : + LLScriptAssetUpload(taskId, itemId, targetType, isRunning, + exerienceId, std::string(), finish, nullptr), + mScriptName(scriptName), + mQueueId(queueId) + { + setAssetId(assetId); + } + + virtual LLSD prepareUpload() + { + /* *NOTE$: The parent class (LLScriptAssetUpload will attempt to save + * the script buffer into to the cache. Since the resource is already in + * the cache we don't want to do that. Just put a compiling message in + * the window and move on + */ + LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", LLSD(mQueueId)); + if (queue) + { + std::string message = std::string("Compiling \"") + getScriptName() + std::string("\"..."); + + queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM); + } + + return LLSDMap("success", LLSD::Boolean(true)); + } + + + std::string getScriptName() const { return mScriptName; } + +private: + void setScriptName(const std::string &scriptName) { mScriptName = scriptName; } + + LLUUID mQueueId; + std::string mScriptName; +}; + +///---------------------------------------------------------------------------- +/// Local function declarations, constants, enums, and typedefs +///---------------------------------------------------------------------------- + +struct LLScriptQueueData +{ + LLUUID mQueueID; + LLUUID mTaskId; + LLPointer<LLInventoryItem> mItem; + LLHost mHost; + LLUUID mExperienceId; + std::string mExperiencename; + LLScriptQueueData(const LLUUID& q_id, const LLUUID& task_id, LLInventoryItem* item) : + mQueueID(q_id), mTaskId(task_id), mItem(new LLInventoryItem(item)) {} + +}; + +///---------------------------------------------------------------------------- +/// Class LLFloaterScriptQueue +///---------------------------------------------------------------------------- + +// Default constructor +LLFloaterScriptQueue::LLFloaterScriptQueue(const LLSD& key) : + LLFloater(key), + mDone(false), + mMono(false) +{ + +} + +// Destroys the object +LLFloaterScriptQueue::~LLFloaterScriptQueue() +{ +} + +bool LLFloaterScriptQueue::postBuild() +{ + childSetAction("close",onCloseBtn,this); + getChildView("close")->setEnabled(false); + setVisible(true); + return true; +} + +// static +void LLFloaterScriptQueue::onCloseBtn(void* user_data) +{ + LLFloaterScriptQueue* self = (LLFloaterScriptQueue*)user_data; + self->closeFloater(); +} + +void LLFloaterScriptQueue::addObject(const LLUUID& id, std::string name) +{ + ObjectData obj = { id, name }; + mObjectList.push_back(obj); +} + +bool LLFloaterScriptQueue::start() +{ + std::string buffer; + + LLStringUtil::format_map_t args; + args["[START]"] = mStartString; + args["[COUNT]"] = llformat ("%d", mObjectList.size()); + buffer = getString ("Starting", args); + + getChild<LLScrollListCtrl>("queue output")->addSimpleElement(buffer, ADD_BOTTOM); + + return startQueue(); +} + +void LLFloaterScriptQueue::addProcessingMessage(const std::string &message, const LLSD &args) +{ + std::string buffer(LLTrans::getString(message, args)); + + getChild<LLScrollListCtrl>("queue output")->addSimpleElement(buffer, ADD_BOTTOM); +} + +void LLFloaterScriptQueue::addStringMessage(const std::string &message) +{ + getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM); +} + + +bool LLFloaterScriptQueue::isDone() const +{ + return (mCurrentObjectID.isNull() && (mObjectList.size() == 0)); +} + +///---------------------------------------------------------------------------- +/// Class LLFloaterCompileQueue +///---------------------------------------------------------------------------- +LLFloaterCompileQueue::LLFloaterCompileQueue(const LLSD& key) + : LLFloaterScriptQueue(key) +{ + setTitle(LLTrans::getString("CompileQueueTitle")); + setStartString(LLTrans::getString("CompileQueueStart")); + +} + +LLFloaterCompileQueue::~LLFloaterCompileQueue() +{ +} + +void LLFloaterCompileQueue::experienceIdsReceived( const LLSD& content ) +{ + for(LLSD::array_const_iterator it = content.beginArray(); it != content.endArray(); ++it) + { + mExperienceIds.insert(it->asUUID()); + } +} + +bool LLFloaterCompileQueue::hasExperience( const LLUUID& id ) const +{ + return mExperienceIds.find(id) != mExperienceIds.end(); +} + +// //Attempt to record this asset ID. If it can not be inserted into the set +// //then it has already been processed so return false. + +void LLFloaterCompileQueue::handleHTTPResponse(std::string pumpName, const LLSD &expresult) +{ + LLEventPumps::instance().post(pumpName, expresult); +} + +// *TODO: handleSCriptRetrieval is passed into the cache via a legacy C function pointer +// future project would be to convert these to C++ callables (std::function<>) so that +// we can use bind and remove the userData parameter. +// +void LLFloaterCompileQueue::handleScriptRetrieval(const LLUUID& assetId, + LLAssetType::EType type, void* userData, S32 status, LLExtStat extStatus) +{ + LLSD result(LLSD::emptyMap()); + + result["asset_id"] = assetId; + if (status) + { + result["error"] = status; + + if (status == LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE) + { + result["message"] = LLTrans::getString("CompileQueueProblemDownloading") + (":"); + result["alert"] = LLTrans::getString("CompileQueueScriptNotFound"); + } + else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) + { + result["message"] = LLTrans::getString("CompileQueueInsufficientPermFor") + (":"); + result["alert"] = LLTrans::getString("CompileQueueInsufficientPermDownload"); + } + else + { + result["message"] = LLTrans::getString("CompileQueueUnknownFailure"); + } + } + + LLEventPumps::instance().post(((HandleScriptUserData *)userData)->getPumpName(), result); + +} + +/*static*/ +void LLFloaterCompileQueue::processExperienceIdResults(LLSD result, LLUUID parent) +{ + LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", parent); + if (!queue) + return; + + queue->experienceIdsReceived(result["experience_ids"]); + + // getDerived handle gets a handle that can be resolved to a parent class of the derived object. + LLHandle<LLFloaterScriptQueue> hFloater(queue->getDerivedHandle<LLFloaterScriptQueue>()); + + // note subtle difference here: getDerivedHandle in this case is for an LLFloaterCompileQueue + fnQueueAction_t fn = boost::bind(LLFloaterCompileQueue::processScript, + queue->getDerivedHandle<LLFloaterCompileQueue>(), _1, _2, _3); + + + LLCoros::instance().launch("ScriptQueueCompile", boost::bind(LLFloaterScriptQueue::objectScriptProcessingQueueCoro, + queue->mStartString, + hFloater, + queue->mObjectList, + fn)); + +} + +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. +bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloater, + const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) +{ + if (LLApp::isExiting()) + { + // Reply from coroutine came on shutdown + // We are quiting, don't start any more coroutines! + return true; + } + + LLSD result; + LLCheckedHandle<LLFloaterCompileQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + bool monocompile = floater->mMono; + + // Initial test to see if we can (or should) attempt to compile the script. + LLInventoryItem *item = dynamic_cast<LLInventoryItem *>(inventory); + + if (!item) + { + LL_WARNS("SCRIPTQ") << "item retrieved is not an LLInventoryItem." << LL_ENDL; + return true; + } + + if (!item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID()) || + !item->getPermissions().allowCopyBy(gAgent.getID(), gAgent.getGroupID())) + { + std::string buffer = "Skipping: " + item->getName() + "(Permissions)"; + floater->addStringMessage(buffer); + return true; + } + + // Attempt to retrieve the experience + LLUUID experienceId; + { + LLExperienceCache::instance().fetchAssociatedExperience(inventory->getParentUUID(), inventory->getUUID(), + boost::bind(&LLFloaterCompileQueue::handleHTTPResponse, pump.getName(), _1)); + + result = llcoro::suspendUntilEventOnWithTimeout(pump, QUEUE_INVENTORY_FETCH_TIMEOUT, + LLSDMap("timeout", LLSD::Boolean(true))); + + floater.check(); + + if (result.has("timeout")) + { // A timeout filed in the result will always be true if present. + LLStringUtil::format_map_t args; + args["[OBJECT_NAME]"] = inventory->getName(); + std::string buffer = floater->getString("Timeout", args); + floater->addStringMessage(buffer); + return true; + } + + if (result.has(LLExperienceCache::EXPERIENCE_ID)) + { + experienceId = result[LLExperienceCache::EXPERIENCE_ID].asUUID(); + if (!floater->hasExperience(experienceId)) + { + floater->addProcessingMessage("CompileNoExperiencePerm", + LLSDMap("SCRIPT", inventory->getName()) + ("EXPERIENCE", result[LLExperienceCache::NAME].asString())); + return true; + } + } + + } + + if (!gAssetStorage) + { + // viewer likely is shutting down + return true; + } + + { + HandleScriptUserData userData(pump.getName()); + + + // request the asset + gAssetStorage->getInvItemAsset(LLHost(), + gAgent.getID(), + gAgent.getSessionID(), + item->getPermissions().getOwner(), + object->getID(), + item->getUUID(), + item->getAssetUUID(), + item->getType(), + &LLFloaterCompileQueue::handleScriptRetrieval, + &userData); + + result = llcoro::suspendUntilEventOnWithTimeout(pump, QUEUE_INVENTORY_FETCH_TIMEOUT, + LLSDMap("timeout", LLSD::Boolean(true))); + } + + if (result.has("timeout")) + { // A timeout filed in the result will always be true if present. + LLStringUtil::format_map_t args; + args["[OBJECT_NAME]"] = inventory->getName(); + std::string buffer = floater->getString("Timeout", args); + floater->addStringMessage(buffer); + return true; + } + + if (result.has("error")) + { + LL_WARNS("SCRIPTQ") << "Inventory fetch returned with error. Code: " << result["error"].asString() << LL_ENDL; + std::string buffer = result["message"].asString() + " " + inventory->getName(); + floater->addStringMessage(buffer); + + if (result.has("alert")) + { + LLSD args; + args["MESSAGE"] = result["alert"].asString(); + LLNotificationsUtil::add("SystemMessage", args); + } + return true; + } + + LLUUID assetId = result["asset_id"]; + + std::string url = object->getRegion()->getCapability("UpdateScriptTask"); + + { + LLResourceUploadInfo::ptr_t uploadInfo(new LLQueuedScriptAssetUpload(object->getID(), + inventory->getUUID(), + assetId, + monocompile ? LLScriptAssetUpload::MONO : LLScriptAssetUpload::LSL2, + true, + inventory->getName(), + LLUUID(), + experienceId, + boost::bind(&LLFloaterCompileQueue::handleHTTPResponse, pump.getName(), _4))); + + LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); + } + + result = llcoro::suspendUntilEventOnWithTimeout(pump, QUEUE_INVENTORY_FETCH_TIMEOUT, LLSDMap("timeout", LLSD::Boolean(true))); + + floater.check(); + + if (result.has("timeout")) + { // A timeout filed in the result will always be true if present. + LLStringUtil::format_map_t args; + args["[OBJECT_NAME]"] = inventory->getName(); + std::string buffer = floater->getString("Timeout", args); + floater->addStringMessage(buffer); + return true; + } + + // Bytecode save completed + if (result["compiled"]) + { + std::string buffer = std::string("Compilation of \"") + inventory->getName() + std::string("\" succeeded"); + + floater->addStringMessage(buffer); + LL_INFOS() << buffer << LL_ENDL; + } + else + { + LLSD compile_errors = result["errors"]; + std::string buffer = std::string("Compilation of \"") + inventory->getName() + std::string("\" failed:"); + floater->addStringMessage(buffer); + for (LLSD::array_const_iterator line = compile_errors.beginArray(); + line < compile_errors.endArray(); line++) + { + std::string str = line->asString(); + str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); + + floater->addStringMessage(str); + } + LL_INFOS() << result["errors"] << LL_ENDL; + } + + return true; +} + +bool LLFloaterCompileQueue::startQueue() +{ + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string lookup_url = region->getCapability("GetCreatorExperiences"); + if (!lookup_url.empty()) + { + LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t success = + boost::bind(&LLFloaterCompileQueue::processExperienceIdResults, _1, getKey().asUUID()); + + LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t failure = + boost::bind(&LLFloaterCompileQueue::processExperienceIdResults, LLSD(), getKey().asUUID()); + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(lookup_url, + success, failure); + return true; + } + } + + return true; +} + + +///---------------------------------------------------------------------------- +/// Class LLFloaterResetQueue +///---------------------------------------------------------------------------- + +LLFloaterResetQueue::LLFloaterResetQueue(const LLSD& key) + : LLFloaterScriptQueue(key) +{ + setTitle(LLTrans::getString("ResetQueueTitle")); + setStartString(LLTrans::getString("ResetQueueStart")); +} + +LLFloaterResetQueue::~LLFloaterResetQueue() +{ +} + +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. +bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, + const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) +{ + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + + std::string buffer; + buffer = floater->getString("Resetting") + (": ") + inventory->getName(); + floater->addStringMessage(buffer); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ScriptReset); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_Script); + msg->addUUIDFast(_PREHASH_ObjectID, object->getID()); + msg->addUUIDFast(_PREHASH_ItemID, inventory->getUUID()); + msg->sendReliable(object->getRegion()->getHost()); + + return true; +} + +bool LLFloaterResetQueue::startQueue() +{ + // Bind the resetObjectScripts method into a QueueAction function and pass it + // into the object queue processing coroutine. + fnQueueAction_t fn = boost::bind(LLFloaterResetQueue::resetObjectScripts, + getDerivedHandle<LLFloaterScriptQueue>(), _1, _2, _3); + + LLCoros::instance().launch("ScriptResetQueue", boost::bind(LLFloaterScriptQueue::objectScriptProcessingQueueCoro, + mStartString, + getDerivedHandle<LLFloaterScriptQueue>(), + mObjectList, + fn)); + + return true; +} + +///---------------------------------------------------------------------------- +/// Class LLFloaterRunQueue +///---------------------------------------------------------------------------- + +LLFloaterRunQueue::LLFloaterRunQueue(const LLSD& key) + : LLFloaterScriptQueue(key) +{ + setTitle(LLTrans::getString("RunQueueTitle")); + setStartString(LLTrans::getString("RunQueueStart")); +} + +LLFloaterRunQueue::~LLFloaterRunQueue() +{ +} + +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. +bool LLFloaterRunQueue::runObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, + const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) +{ + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + + std::string buffer; + buffer = floater->getString("Running") + (": ") + inventory->getName(); + floater->addStringMessage(buffer); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_SetScriptRunning); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_Script); + msg->addUUIDFast(_PREHASH_ObjectID, object->getID()); + msg->addUUIDFast(_PREHASH_ItemID, inventory->getUUID()); + msg->addBOOLFast(_PREHASH_Running, true); + msg->sendReliable(object->getRegion()->getHost()); + + return true; +} + +bool LLFloaterRunQueue::startQueue() +{ + LLHandle<LLFloaterScriptQueue> hFloater(getDerivedHandle<LLFloaterScriptQueue>()); + fnQueueAction_t fn = boost::bind(LLFloaterRunQueue::runObjectScripts, hFloater, _1, _2, _3); + + LLCoros::instance().launch("ScriptRunQueue", boost::bind(LLFloaterScriptQueue::objectScriptProcessingQueueCoro, + mStartString, + hFloater, + mObjectList, + fn)); + + return true; +} + + +///---------------------------------------------------------------------------- +/// Class LLFloaterNotRunQueue +///---------------------------------------------------------------------------- + +LLFloaterNotRunQueue::LLFloaterNotRunQueue(const LLSD& key) + : LLFloaterScriptQueue(key) +{ + setTitle(LLTrans::getString("NotRunQueueTitle")); + setStartString(LLTrans::getString("NotRunQueueStart")); +} + +LLFloaterNotRunQueue::~LLFloaterNotRunQueue() +{ +} + +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. +bool LLFloaterNotRunQueue::stopObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, + const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) +{ + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + + std::string buffer; + buffer = floater->getString("NotRunning") + (": ") + inventory->getName(); + floater->addStringMessage(buffer); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_SetScriptRunning); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_Script); + msg->addUUIDFast(_PREHASH_ObjectID, object->getID()); + msg->addUUIDFast(_PREHASH_ItemID, inventory->getUUID()); + msg->addBOOLFast(_PREHASH_Running, false); + msg->sendReliable(object->getRegion()->getHost()); + + return true; +} + +bool LLFloaterNotRunQueue::startQueue() +{ + LLHandle<LLFloaterScriptQueue> hFloater(getDerivedHandle<LLFloaterScriptQueue>()); + + fnQueueAction_t fn = boost::bind(&LLFloaterNotRunQueue::stopObjectScripts, hFloater, _1, _2, _3); + LLCoros::instance().launch("ScriptQueueNotRun", boost::bind(LLFloaterScriptQueue::objectScriptProcessingQueueCoro, + mStartString, + hFloater, + mObjectList, + fn)); + + return true; +} + +///---------------------------------------------------------------------------- +/// Local function definitions +///---------------------------------------------------------------------------- +void ObjectInventoryFetcher::inventoryChanged(LLViewerObject* object, + LLInventoryObject::object_list_t* inventory, S32 serial_num, void* user_data) +{ + mInventoryList.clear(); + mInventoryList.assign(inventory->begin(), inventory->end()); + + mPump.post(LLSDMap("changed", LLSD::Boolean(true))); + +} + +void LLFloaterScriptQueue::objectScriptProcessingQueueCoro(std::string action, LLHandle<LLFloaterScriptQueue> hfloater, + object_data_list_t objectList, fnQueueAction_t func) +{ + LLCoros::set_consuming(true); + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // This is expected if the dialog closes. + LLEventMailDrop maildrop(QUEUE_EVENTPUMP_NAME, true); + + try + { + for (object_data_list_t::iterator itObj(objectList.begin()); (itObj != objectList.end()); ++itObj) + { + bool firstForObject = true; + LLUUID object_id = (*itObj).mObjectId; + LL_INFOS("SCRIPTQ") << "Next object in queue with ID=" << object_id.asString() << LL_ENDL; + + LLPointer<LLViewerObject> obj = gObjectList.findObject(object_id); + LLInventoryObject::object_list_t inventory; + if (obj) + { + ObjectInventoryFetcher::ptr_t fetcher(new ObjectInventoryFetcher(maildrop, obj, NULL)); + + fetcher->fetchInventory(); + + LLStringUtil::format_map_t args; + args["[OBJECT_NAME]"] = (*itObj).mObjectName; + floater->addStringMessage(floater->getString("LoadingObjInv", args)); + + LLSD result = llcoro::suspendUntilEventOnWithTimeout(maildrop, QUEUE_INVENTORY_FETCH_TIMEOUT, + LLSDMap("timeout", LLSD::Boolean(true))); + + if (result.has("timeout")) + { // A timeout filed in the result will always be true if present. + LL_WARNS("SCRIPTQ") << "Unable to retrieve inventory for object " << object_id.asString() << + ". Skipping to next object." << LL_ENDL; + + LLStringUtil::format_map_t args; + args["[OBJECT_NAME]"] = (*itObj).mObjectName; + floater->addStringMessage(floater->getString("Timeout", args)); + + continue; + } + + inventory.assign(fetcher->getInventoryList().begin(), fetcher->getInventoryList().end()); + } + else + { + LL_WARNS("SCRIPTQ") << "Unable to retrieve object with ID of " << object_id << + ". Skipping to next." << LL_ENDL; + continue; + } + + // TODO: Get the name of the object we are looking at here so that we can display it below. + //std::string objName = (dynamic_cast<LLInventoryObject *>(obj.get()))->getName(); + LL_DEBUGS("SCRIPTQ") << "Object has " << inventory.size() << " items." << LL_ENDL; + + for (LLInventoryObject::object_list_t::iterator itInv = inventory.begin(); + itInv != inventory.end(); ++itInv) + { + floater.check(); + + // note, we have a smart pointer to the obj above... but if we didn't we'd check that + // it still exists here. + + if (((*itInv)->getType() == LLAssetType::AT_LSL_TEXT)) + { + LL_DEBUGS("SCRIPTQ") << "Inventory item " << (*itInv)->getUUID().asString() << "\"" << (*itInv)->getName() << "\"" << LL_ENDL; + if (firstForObject) + { + //floater->addStringMessage(objName + ":"); + firstForObject = false; + } + + if (!func(obj, (*itInv), maildrop)) + { + continue; + } + } + + // no other explicit suspension point in this loop. func(...) MIGHT suspend + // but offers no guarantee of doing so. + llcoro::suspend(); + } + floater.check(); + } + + floater->addStringMessage("Done"); + floater->getChildView("close")->setEnabled(true); + } + catch (LLCheckedHandleBase::Stale &) + { + // This is expected. It means that floater has been closed before + // processing was completed. + LL_DEBUGS("SCRIPTQ") << "LLExeceptionStaleHandle caught! Floater has most likely been closed." << LL_ENDL; + } +} |