From 13560e292eef7108b71bda895dfeb4c36db142f4 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 22 May 2011 22:01:11 +0200 Subject: [PATCH] Added AIFetchInventoryFolder and more. Added a new statemachine AIFetchInventoryFolder, which can be used to fetch the contents of a folder by name or UUID. Also added AIEvent (and one event, AIEvent::LLInventoryModel_mIsAgentInvUsable_true, which is needed for AIFetchInventoryFolder). Fixed LLInventoryModel::sBackgroundFetchActive to correctly reflect whether or not LLInventoryModel::backgroundFetch is added to gIdleCallbacks. Avoid duplicated entries in sFetchQueue. Reset sFullFetchStarted in LLInventoryModel::stopBackgroundFetch to allow for a renewed full fetch when some single-folder fetch stops it. Added AIStateMachine::mQueued to make calling 'cont()' more robust: calling cont() / idle() / cont() on a row would otherwise add a statemachine twice to the active list, which would cause a crash when it's killed. --- indra/llcommon/aithreadsafe.h | 3 +- indra/newview/llhudeffectlookat.h | 1 + indra/newview/llinventorymodel.cpp | 43 +++- indra/newview/llinventorymodel.h | 4 + indra/newview/llviewerobject.h | 1 + indra/newview/statemachine/CMakeLists.txt | 11 +- indra/newview/statemachine/aievent.cpp | 113 ++++++++++ indra/newview/statemachine/aievent.h | 78 +++++++ .../statemachine/aifetchinventoryfolder.cpp | 193 ++++++++++++++++++ .../statemachine/aifetchinventoryfolder.h | 152 ++++++++++++++ indra/newview/statemachine/aistatemachine.cpp | 10 + indra/newview/statemachine/aistatemachine.h | 5 +- 12 files changed, 603 insertions(+), 11 deletions(-) create mode 100644 indra/newview/statemachine/aievent.cpp create mode 100644 indra/newview/statemachine/aievent.h create mode 100644 indra/newview/statemachine/aifetchinventoryfolder.cpp create mode 100644 indra/newview/statemachine/aifetchinventoryfolder.h diff --git a/indra/llcommon/aithreadsafe.h b/indra/llcommon/aithreadsafe.h index 3963281cb..52c4ac8d2 100644 --- a/indra/llcommon/aithreadsafe.h +++ b/indra/llcommon/aithreadsafe.h @@ -403,7 +403,8 @@ protected: // Locking control. LLMutex mMutex; - // For use by AIThreadSafeSimpleDC + friend struct AIRegisteredStateMachinesList; + // For use by AIThreadSafeSimpleDC and AIRegisteredStateMachinesList. AIThreadSafeSimple(void) { } AIThreadSafeSimple(AIAPRPool& parent) : mMutex(parent) { } diff --git a/indra/newview/llhudeffectlookat.h b/indra/newview/llhudeffectlookat.h index 922c718f6..97de0e8ba 100644 --- a/indra/newview/llhudeffectlookat.h +++ b/indra/newview/llhudeffectlookat.h @@ -34,6 +34,7 @@ #define LL_LLHUDEFFECTLOOKAT_H #include "llhudeffect.h" +#include "llframetimer.h" class LLViewerObject; class LLVOAvatar; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 910d94baf..1cc9393d7 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -59,6 +59,7 @@ #include "llviewercontrol.h" #include "llvoavatar.h" #include "llsdutil.h" +#include "statemachine/aievent.h" // #include "llappviewer.h" // gLostItemsRoot // @@ -349,6 +350,13 @@ void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id, items = get_ptr_in_map(mParentChildItemTree, cat_id); } +// Same but just categories. +void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id, + cat_array_t*& categories) const +{ + categories = get_ptr_in_map(mParentChildCategoryTree, cat_id); +} + // SJB: Added version to lock the arrays to catch potential logic bugs void LLInventoryModel::lockDirectDescendentArrays(const LLUUID& cat_id, cat_array_t*& categories, @@ -1727,7 +1735,6 @@ void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id) { if (!sAllFoldersFetched) { - sBackgroundFetchActive = TRUE; if (cat_id.isNull()) { if (!sFullFetchStarted) @@ -1735,15 +1742,29 @@ void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id) sFullFetchStarted = TRUE; sFetchQueue.push_back(gInventoryLibraryRoot); sFetchQueue.push_back(gAgent.getInventoryRootID()); - gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); + if (!sBackgroundFetchActive) + { + sBackgroundFetchActive = TRUE; + gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); + } } } else { // specific folder requests go to front of queue - if (sFetchQueue.empty() || sFetchQueue.front() != cat_id) + // Remove it from the queue first, to avoid getting it twice. + if (!sFetchQueue.empty() && sFetchQueue.front() != cat_id) { - sFetchQueue.push_front(cat_id); + std::deque::iterator old_entry = std::find(sFetchQueue.begin(), sFetchQueue.end(), cat_id); + if (old_entry != sFetchQueue.end()) + { + sFetchQueue.erase(old_entry); + } + } + sFetchQueue.push_front(cat_id); + if (!sBackgroundFetchActive) + { + sBackgroundFetchActive = TRUE; gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); } } @@ -1753,9 +1774,12 @@ void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id) //static void LLInventoryModel::findLostItems() { - sBackgroundFetchActive = TRUE; sFetchQueue.push_back(LLUUID::null); - gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); + if (!sBackgroundFetchActive) + { + sBackgroundFetchActive = TRUE; + gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); + } } //static @@ -1767,7 +1791,11 @@ void LLInventoryModel::stopBackgroundFetch() gIdleCallbacks.deleteFunction(&LLInventoryModel::backgroundFetch, NULL); sBulkFetchCount=0; sMinTimeBetweenFetches=0.0f; -// sFullFetchStarted=FALSE; + if (!sAllFoldersFetched) + { + // We didn't finish this, so set it to FALSE in order to be able to start it again. + sFullFetchStarted=FALSE; + } } } @@ -2941,6 +2969,7 @@ void LLInventoryModel::buildParentChildMap() // root of the agent's inv found. // The inv tree is built. mIsAgentInvUsable = true; + AIEvent::trigger(AIEvent::LLInventoryModel_mIsAgentInvUsable_true); } } llinfos << " finished buildParentChildMap " << llendl; diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 6fdb59d1a..a6dc0f31a 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -38,6 +38,7 @@ #include "lluuid.h" #include "llpermissionsflags.h" #include "llstring.h" +#include "llhttpclient.h" #include #include @@ -199,6 +200,9 @@ public: void getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t*& categories, item_array_t*& items) const; + // Same but only get categories. + void getDirectDescendentsOf(const LLUUID& cat_id, + cat_array_t*& categories) const; // Starting with the object specified, add its descendents to the // array provided, but do not add the inventory object specified diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 4833b96d8..2a08b5f50 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -73,6 +73,7 @@ class LLViewerPartSourceScript; class LLViewerRegion; class LLViewerObjectMedia; class LLVOInventoryListener; +class LLPartSysData; typedef enum e_object_update_type { diff --git a/indra/newview/statemachine/CMakeLists.txt b/indra/newview/statemachine/CMakeLists.txt index b11f1c3e1..a95431b0f 100644 --- a/indra/newview/statemachine/CMakeLists.txt +++ b/indra/newview/statemachine/CMakeLists.txt @@ -13,7 +13,9 @@ include(LLWindow) include(LLUI) include(LLRender) include(LLImage) - +include(LLCharacter) +include(LLInventory) +include(LLPrimitive) include_directories( ${CMAKE_SOURCE_DIR}/newview @@ -27,11 +29,16 @@ include_directories( ${LLUI_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLIMAGE_INCLUDE_DIRS} + ${LLCHARACTER_INCLUDE_DIRS} + ${LLINVENTORY_INCLUDE_DIRS} + ${LLPRIMITIVE_INCLUDE_DIRS} ) set(statemachine_SOURCE_FILES aistatemachine.cpp aifilepicker.cpp + aifetchinventoryfolder.cpp + aievent.cpp ) set(statemachine_HEADER_FILES @@ -39,6 +46,8 @@ set(statemachine_HEADER_FILES aistatemachine.h aifilepicker.h aidirpicker.h + aifetchinventoryfolder.h + aievent.h ) set_source_files_properties(${statemachine_HEADER_FILES} diff --git a/indra/newview/statemachine/aievent.cpp b/indra/newview/statemachine/aievent.cpp new file mode 100644 index 000000000..0e7e9fa6e --- /dev/null +++ b/indra/newview/statemachine/aievent.cpp @@ -0,0 +1,113 @@ +/** + * @file aievent.cpp + * @brief Implementation of AIEvent. + * + * Copyright (c) 2011, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 19/05/2011 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "linden_common.h" +#include "aievent.h" +#include "aistatemachine.h" +#include + +// Additional information stored per registered statemachine in AIRegisteredStateMachines std::map. +struct AIRSData { + bool mOneShot; + AIRSData(void) { } + AIRSData(bool one_shot) : mOneShot(one_shot) { } +}; + +// A list of all statemachines registered for a particular event, and and API to work on it. +struct AIRegisteredStateMachines { + typedef std::map rsm_type; + rsm_type mRegisteredStateMachines; + void Register(AIStateMachine* statemachine, bool one_shot) { mRegisteredStateMachines[statemachine] = one_shot; } + void Unregister(AIStateMachine* statemachine) { mRegisteredStateMachines.erase(statemachine); } + void trigger(void); +}; + +// Inline this, because it's only called from one place. +inline void AIRegisteredStateMachines::trigger(void) +{ + rsm_type::iterator sm = mRegisteredStateMachines.begin(); + while(sm != mRegisteredStateMachines.end()) + { + sm->first->cont(); // This is safe, cont() does never access mRegisteredStateMachines. + if (sm->second.mOneShot) + mRegisteredStateMachines.erase(sm++); + else + ++sm; + } +} + +// A list (array) with all AIRegisteredStateMachines maps, one for each event type. +struct AIRegisteredStateMachinesList { + AIThreadSafeSimple mRegisteredStateMachinesList[AIEvent::number_of_events]; + AIRegisteredStateMachinesList(void); + AIThreadSafeSimple& operator[](AIEvent::AIEvents event) { return mRegisteredStateMachinesList[event]; } +}; + +AIRegisteredStateMachinesList::AIRegisteredStateMachinesList(void) +{ + for (int event = 0; event < AIEvent::number_of_events; ++event) + { + new (&mRegisteredStateMachinesList[event]) AIRegisteredStateMachines; + } +} + +// Instantiate the list with all AIRegisteredStateMachines maps. +static AIRegisteredStateMachinesList registered_statemachines_list; + +//----------------------------------------------------------------------------- +// External API starts here. +// Each function obtains access to the thread-safe AIRegisteredStateMachines that belongs +// to the given event (locking it's mutex) and then calls the corresponding method. + +// Register a statemachine for notification of event. +// static +void AIEvent::Register(AIEvents event, AIStateMachine* statemachine, bool one_shot) +{ + statemachine->idle(); + AIAccess registered_statemachines_w(registered_statemachines_list[event]); + registered_statemachines_w->Register(statemachine, one_shot); +} + +// Unregister a statemachine for notification of event. +// static +void AIEvent::Unregister(AIEvents event, AIStateMachine* statemachine) +{ + AIAccess registered_statemachines_w(registered_statemachines_list[event]); + registered_statemachines_w->Unregister(statemachine); +} + +// Trigger event. +// static +void AIEvent::trigger(AIEvents event) +{ + AIAccess registered_statemachines_w(registered_statemachines_list[event]); + registered_statemachines_w->trigger(); +} + diff --git a/indra/newview/statemachine/aievent.h b/indra/newview/statemachine/aievent.h new file mode 100644 index 000000000..a90a04aaf --- /dev/null +++ b/indra/newview/statemachine/aievent.h @@ -0,0 +1,78 @@ +/** + * @file aievent.h + * @brief Collect viewer events for statemachines. + * + * Copyright (c) 2011, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 19/05/2011 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AIEVENT_H +#define AIEVENT_H + +class AIStateMachine; + +class AIEvent { + public: + enum AIEvents { + LLInventoryModel_mIsAgentInvUsable_true, // LLInventoryModel::mIsAgentInvUsable was set to true. + number_of_events + }; + + /** + * Register a statemachine for the given event. + * + * If the event happens then statemachine->cont() is called. + * + * @param event the event that we want to be notified of. + * @param statemachine the statemachine. + * @param one_shot if true then AIEvent::Unregister is called automatically when the event occured once. + */ + static void Register(AIEvents event, AIStateMachine* statemachine, bool one_shot = true); + + /** + * Unregister a previously registered event. + * + * This may be called for already unregistered events. + * This should be called from the destructor of a statemachine for any event it registers, + * as well as when it doesn't need the event anymore (in the case on non- one shot events). + * + * @param event the event we want to no longer be notified off. + * @param statemachine the statemachine. + */ + static void Unregister(AIEvents event, AIStateMachine* statemachine); + + /** + * Trigger the event. + * + * This is called by the viewer code when the event happens and + * causes statemachine->cont() to be called for every registered + * statemachine. + * + * @param event the event that just happened. + */ + static void trigger(AIEvents event); +}; + +#endif diff --git a/indra/newview/statemachine/aifetchinventoryfolder.cpp b/indra/newview/statemachine/aifetchinventoryfolder.cpp new file mode 100644 index 000000000..3a3865a6e --- /dev/null +++ b/indra/newview/statemachine/aifetchinventoryfolder.cpp @@ -0,0 +1,193 @@ +/** + * @file aifetchinventoryfolder.cpp + * @brief Implementation of AIFetchInventoryFolder + * + * Copyright (c) 2011, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 19/05/2011 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "linden_common.h" +#include "aifetchinventoryfolder.h" +#include "aievent.h" +#include "llagent.h" + +enum fetchinventoryfolder_state_type { + AIFetchInventoryFolder_checkFolderExists = AIStateMachine::max_state, + AIFetchInventoryFolder_fetchDescendents, + AIFetchInventoryFolder_folderCompleted +}; + +class AIInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver { + public: + AIInventoryFetchDescendentsObserver(AIStateMachine* statemachine, LLUUID const& folder); + ~AIInventoryFetchDescendentsObserver() { gInventory.removeObserver(this); } + + protected: + /*virtual*/ void done() + { + mStateMachine->set_state(AIFetchInventoryFolder_folderCompleted); + delete this; + } + + private: + AIStateMachine* mStateMachine; +}; + +AIInventoryFetchDescendentsObserver::AIInventoryFetchDescendentsObserver(AIStateMachine* statemachine, LLUUID const& folder) : mStateMachine(statemachine) +{ + mStateMachine->idle(); + folder_ref_t folders(1, folder); + fetchDescendents(folders); + gInventory.addObserver(this); + if (isEverythingComplete()) + done(); +} + +void AIFetchInventoryFolder::fetch(std::string const& foldername, bool create, bool fetch_contents) +{ + fetch(gAgent.getInventoryRootID(), foldername, create, fetch_contents); +} + +char const* AIFetchInventoryFolder::state_str_impl(state_type run_state) const +{ + switch(run_state) + { + AI_CASE_RETURN(AIFetchInventoryFolder_checkFolderExists); + AI_CASE_RETURN(AIFetchInventoryFolder_fetchDescendents); + AI_CASE_RETURN(AIFetchInventoryFolder_folderCompleted); + } + return "UNKNOWN STATE"; +} + +void AIFetchInventoryFolder::initialize_impl(void) +{ + mExists = false; + mCreated = false; + mNeedNotifyObservers = false; + set_state(AIFetchInventoryFolder_checkFolderExists); + if (!gInventory.isInventoryUsable()) + AIEvent::Register(AIEvent::LLInventoryModel_mIsAgentInvUsable_true, this); +} + +void AIFetchInventoryFolder::multiplex_impl(void) +{ + switch (mRunState) + { + case AIFetchInventoryFolder_checkFolderExists: + { + // If LLInventoryModel_mIsAgentInvUsable_true then this should be and stay true forever. + llassert(gInventory.isInventoryUsable()); + if (mParentFolder.isNull()) + mParentFolder = gAgent.getInventoryRootID(); + if (mFolderUUID.isNull() || !gInventory.getCategory(mFolderUUID)) // Is the UUID unknown, or doesn't exist? + { + // Set this to null here in case we abort. + mFolderUUID.setNull(); + if (mFolderName.empty()) + { + // We can only find a folder by name, or create it, if we know it's name. + llwarns << "Unknown folder ID " << mFolderUUID << llendl; + abort(); + break; + } + // Check if the parent exists. + if (mParentFolder != gAgent.getInventoryRootID() && !gInventory.getCategory(mParentFolder)) + { + llwarns << "Unknown parent folder ID " << mParentFolder << llendl; + abort(); + break; + } + // Look up UUID by name. + LLInventoryModel::cat_array_t* categories; + gInventory.getDirectDescendentsOf(mParentFolder, categories); + for (S32 i = 0; i < categories->getLength(); ++i) + { + LLPointer const& category(categories->get(i)); + if (category->getName() == mFolderName) + { + mFolderUUID = category->getUUID(); + break; + } + } + if (mFolderUUID.isNull()) // Does the folder exist? + { + if (!mCreate) + { + // We're done. + finish(); + break; + } + // Create the folder. + mFolderUUID = gInventory.createNewCategory(mParentFolder, LLAssetType::AT_NONE, mFolderName); + llassert_always(!mFolderUUID.isNull()); + Dout(dc::statemachine, "Created folder \"" << mFolderName << "\"."); + mNeedNotifyObservers = true; + } + mCreated = true; + } + // mFolderUUID is now valid. + mExists = true; + if (!mFetchContents || // No request to fetch contents. + LLInventoryModel::isEverythingFetched()) // No need to fetch contents. + { + // We're done. + finish(); + break; + } + set_state(AIFetchInventoryFolder_fetchDescendents); + /*Fall-through*/ + } + case AIFetchInventoryFolder_fetchDescendents: + { + // This sets the state to AIFetchInventoryFolder_folderCompleted once the folder is complete. + new AIInventoryFetchDescendentsObserver(this, mFolderUUID); + break; + } + case AIFetchInventoryFolder_folderCompleted: + { + // Does it still exist? + if (!gInventory.getCategory(mFolderUUID)) + { + // Assume the folder was deleted in the meantime. + abort(); + break; + } + llassert(gInventory.isCategoryComplete(mFolderUUID)); + // The folder is complete! + finish(); + break; + } + } +} + +void AIFetchInventoryFolder::abort_impl(void) +{ +} + +void AIFetchInventoryFolder::finish_impl(void) +{ + if (mNeedNotifyObservers) + gInventory.notifyObservers(); +} diff --git a/indra/newview/statemachine/aifetchinventoryfolder.h b/indra/newview/statemachine/aifetchinventoryfolder.h new file mode 100644 index 000000000..c83c3ef1b --- /dev/null +++ b/indra/newview/statemachine/aifetchinventoryfolder.h @@ -0,0 +1,152 @@ +/** + * @file aifetchinventoryfolder.h + * @brief Fetch an inventory folder + * + * Copyright (c) 2011, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 19/05/2011 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AIFETCHINVENTORYFOLDER_H +#define AIFETCHINVENTORYFOLDER_H + +#include "aistatemachine.h" +#include "lluuid.h" +#include + +// An inventory folder fetch state machine. +// +// Before calling run(), call fetch() to pass needed parameters. +// +// When the state machine finishes, call aborted() to check +// whether or not the statemachine succeeded in fetching +// the folder or not. +// +// Objects of this type can be reused multiple times, see +// also the documentation of AIStateMachine. +class AIFetchInventoryFolder : public AIStateMachine { + private: + std::string mFolderName; //!< Input variable. + bool mCreate; //!< Input variable: create mFolderName if it doesn't exist. + bool mFetchContents; //!< Input variable: fetch contents before finishing. + LLUUID mParentFolder; //!< Input variable: the UUID of the parent folder. + LLUUID mFolderUUID; //!< Input and/or output variable. + bool mExists; //!< Output variable: true if the folder exists. + bool mCreated; //!< Output variable: true if mFolderName didn't exist and was created by this object. + + bool mNeedNotifyObservers; + + public: + AIFetchInventoryFolder(void) : mCreate(false), mFetchContents(false), mExists(false), mCreated(false) + { Dout(dc::statemachine, "Calling AIFetchInventoryFolder constructor [" << (void*)this << "]"); } + + /** + * @brief Fetch an inventory folder by name, optionally creating it. + * + * Upon successful finish (aborted() returns false), exists() will return true + * if the folder exists; created() will return true if it was created; + * UUID() will return the UUID of the folder. It will then also be possible + * to scan over all folders (Category) of this folder. If fetch_contents + * is set, you will also be able to scan over the contents of the folder + * upon successful finish. + * + * @param parentUUID The UUID of the parent. Passing gAgent.getInventoryRootID(), or a null ID, will assume a root folder. + * @param foldername The name of the folder. + * @param create if set, create the folder if it doesn't exists yet. + * @param fetch_contents if set, fetch the contents before finishing. + */ + void fetch(LLUUID const& parentUUID, std::string const& foldername, bool create = false, bool fetch_contents = true) + { + mParentFolder = parentUUID; + mFetchContents = fetch_contents; + if (mFolderName != foldername) + { + mFolderName = foldername; + mFolderUUID.setNull(); + } + } + + /** + * @brief Fetch an inventory folder by name, optionally creating it. + * + * Upon successful finish (aborted() returns false), exists() will return + * true if the folder exists; created() will return true if it was created; + * UUID() will return the UUID of the folder. It will then also be possible + * to scan over all folders (Category) of this folder. If fetch_contents + * is set, you will also be able to scan over the contents of the folder + * upon successful finish. + * + * @param foldername The name of the folder. + * @param create if set, create the folder if it doesn't exists yet. + * @param fetch_contents if set, fetch the contents before finishing. + */ + void fetch(std::string const& foldername, bool create = false, bool fetch_contents = true); + + /** + * @brief Fetch an inventory folder by UUID. + * + * Upon successful finish (aborted() returns false), exists() will return true + * if the folder exists; it will then be possible to scan over all folders (Category) + * of this folder. If fetch_contents is set, you will also be able to scan over + * the contents of the folder upon successful finish. + * + * @param folderUUID The UUID of the folder. + * @param fetch_contents if set, fetch the contents before finishing. + */ + void fetch(LLUUID const& folderUUID, bool fetch_contents = true) + { + mFetchContents = fetch_contents; + if (mFolderUUID != folderUUID) + { + mFolderName.clear(); + mFolderUUID = folderUUID; + } + } + + std::string const& name(void) const { return mFolderName; } + bool exists(void) const { return mExists; } + bool created(void) const { return mCreated; } + LLUUID const& UUID(void) const { llassert(mExists || mFolderUUID.isNull()); return mFolderUUID; } + + protected: + // Call finish() (or abort()), not delete. + /*virtual*/ ~AIFetchInventoryFolder() { Dout(dc::statemachine, "Calling ~AIFetchInventoryFolder() [" << (void*)this << "]"); } + + // Handle initializing the object. + /*virtual*/ void initialize_impl(void); + + // Handle mRunState. + /*virtual*/ void multiplex_impl(void); + + // Handle aborting from current bs_run state. + /*virtual*/ void abort_impl(void); + + // Handle cleaning up from initialization (or post abort) state. + /*virtual*/ void finish_impl(void); + + // Implemenation of state_str for run states. + /*virtual*/ char const* state_str_impl(state_type run_state) const; +}; + +#endif diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index 6d128b467..b950fc236 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -132,6 +132,8 @@ void AIStateMachine::cont(void) DoutEntering(dc::statemachine, "AIStateMachine::cont() [" << (void*)this << "]"); llassert(mIdle); mIdle = false; + if (mQueued) + return; AIWriteAccess cscm_w(continued_statemachines_and_calling_mainloop); cscm_w->continued_statemachines.push_back(this); if (!cscm_w->calling_mainloop) @@ -140,6 +142,7 @@ void AIStateMachine::cont(void) cscm_w->calling_mainloop = true; gIdleCallbacks.addFunction(&AIStateMachine::mainloop); } + mQueued = true; } void AIStateMachine::set_state(state_type state) @@ -214,6 +217,12 @@ void AIStateMachine::kill(void) { // Should only be called from finish(). llassert(mIdle && (mState == bs_initialize || mState == bs_finish)); + if (mState == bs_initialize) + { + // Bump the statemachine onto the active statemachine list, or else it won't be deleted. + cont(); + idle(); + } mState = bs_killed; } @@ -288,6 +297,7 @@ void AIStateMachine::mainloop(void*) nonempty = true; active_statemachines.push_back(QueueElement(*iter)); Dout(dc::statemachine, "Adding " << (void*)*iter << " to active_statemachines"); + (*iter)->mQueued = false; } if (nonempty) AIWriteAccess(cscm_r)->continued_statemachines.clear(); diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h index 4e041adff..cc0b63ce1 100644 --- a/indra/newview/statemachine/aistatemachine.h +++ b/indra/newview/statemachine/aistatemachine.h @@ -184,6 +184,7 @@ class AIStateMachine { base_state_type mState; //!< State of the base class. bool mIdle; //!< True if this state machine is not running. bool mAborted; //!< True after calling abort() and before calling run(). + bool mQueued; //!< True when the statemachine is queued to be added back to the active list. S64 mSleep; //!< Non-zero while the state machine is sleeping. // Callback facilities. @@ -211,11 +212,11 @@ class AIStateMachine { public: //! Create a non-running state machine. - AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mSleep(0), mParent(NULL), mCallback(NULL) { updateSettings(); } + AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mQueued(false), mSleep(0), mParent(NULL), mCallback(NULL) { updateSettings(); } protected: //! The user should call 'kill()', not delete a AIStateMachine (derived) directly. - virtual ~AIStateMachine() { llassert(mState == bs_killed); } + virtual ~AIStateMachine() { llassert(mState == bs_killed && !mQueued); } public: //! Halt the state machine until cont() is called.