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.
This commit is contained in:
@@ -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) { }
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#define LL_LLHUDEFFECTLOOKAT_H
|
||||
|
||||
#include "llhudeffect.h"
|
||||
#include "llframetimer.h"
|
||||
|
||||
class LLViewerObject;
|
||||
class LLVOAvatar;
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
#include "llviewercontrol.h"
|
||||
#include "llvoavatar.h"
|
||||
#include "llsdutil.h"
|
||||
#include "statemachine/aievent.h"
|
||||
// <edit>
|
||||
#include "llappviewer.h" // gLostItemsRoot
|
||||
// </edit>
|
||||
@@ -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<LLUUID>::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;
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "lluuid.h"
|
||||
#include "llpermissionsflags.h"
|
||||
#include "llstring.h"
|
||||
#include "llhttpclient.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -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
|
||||
|
||||
@@ -73,6 +73,7 @@ class LLViewerPartSourceScript;
|
||||
class LLViewerRegion;
|
||||
class LLViewerObjectMedia;
|
||||
class LLVOInventoryListener;
|
||||
class LLPartSysData;
|
||||
|
||||
typedef enum e_object_update_type
|
||||
{
|
||||
|
||||
@@ -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}
|
||||
|
||||
113
indra/newview/statemachine/aievent.cpp
Normal file
113
indra/newview/statemachine/aievent.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <map>
|
||||
|
||||
// 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<AIStateMachine*, AIRSData> 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<AIRegisteredStateMachines> mRegisteredStateMachinesList[AIEvent::number_of_events];
|
||||
AIRegisteredStateMachinesList(void);
|
||||
AIThreadSafeSimple<AIRegisteredStateMachines>& 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<AIRegisteredStateMachines> 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<AIRegisteredStateMachines> registered_statemachines_w(registered_statemachines_list[event]);
|
||||
registered_statemachines_w->Unregister(statemachine);
|
||||
}
|
||||
|
||||
// Trigger event.
|
||||
// static
|
||||
void AIEvent::trigger(AIEvents event)
|
||||
{
|
||||
AIAccess<AIRegisteredStateMachines> registered_statemachines_w(registered_statemachines_list[event]);
|
||||
registered_statemachines_w->trigger();
|
||||
}
|
||||
|
||||
78
indra/newview/statemachine/aievent.h
Normal file
78
indra/newview/statemachine/aievent.h
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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
|
||||
193
indra/newview/statemachine/aifetchinventoryfolder.cpp
Normal file
193
indra/newview/statemachine/aifetchinventoryfolder.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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<LLViewerInventoryCategory> 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();
|
||||
}
|
||||
152
indra/newview/statemachine/aifetchinventoryfolder.h
Normal file
152
indra/newview/statemachine/aifetchinventoryfolder.h
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <map>
|
||||
|
||||
// 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
|
||||
@@ -132,6 +132,8 @@ void AIStateMachine::cont(void)
|
||||
DoutEntering(dc::statemachine, "AIStateMachine::cont() [" << (void*)this << "]");
|
||||
llassert(mIdle);
|
||||
mIdle = false;
|
||||
if (mQueued)
|
||||
return;
|
||||
AIWriteAccess<cscm_type> 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_type>(cscm_r)->continued_statemachines.clear();
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user