Make AO not only remember current set name, but also current animation for each state

master
Ansariel 2025-11-30 16:23:22 +01:00
parent 6aff026508
commit 8da32f68dc
5 changed files with 99 additions and 65 deletions

View File

@ -40,10 +40,11 @@
#include "llnotificationsutil.h" #include "llnotificationsutil.h"
#include "llstring.h" #include "llstring.h"
#include "llviewercontrol.h" #include "llviewercontrol.h"
#include "llxorcipher.h"
#define ROOT_AO_FOLDER "#AO" #define ROOT_AO_FOLDER "#AO"
#include <boost/graph/graph_concepts.hpp>
static const LLUUID ENCRYPTION_MAGIC_ID("4b552ff5-fd63-408c-8288-cd09429852ba");
constexpr F32 INVENTORY_POLLING_INTERVAL = 5.0f; constexpr F32 INVENTORY_POLLING_INTERVAL = 5.0f;
AOEngine::AOEngine() : AOEngine::AOEngine() :
@ -68,6 +69,28 @@ AOEngine::AOEngine() :
AOEngine::~AOEngine() AOEngine::~AOEngine()
{ {
LLSD currentState = LLSD().with("CurrentSet", mCurrentSet->getName());
LLSD currentAnimations;
for (S32 index = 0; index < AOSet::AOSTATES_MAX; ++index)
{
if (auto state = mCurrentSet->getState(index); state && !state->mAnimations.empty())
{
LL_DEBUGS("AOEngine") << "Storing current animation for state " << index << ": Animation index " << state->mCurrentAnimation << LL_ENDL;
LLUUID shadow_id{ state->mAnimations[state->mCurrentAnimation].mAssetUUID };
LLXORCipher cipher(ENCRYPTION_MAGIC_ID.mData, UUID_BYTES);
cipher.encrypt(shadow_id.mData, UUID_BYTES);
currentAnimations.insert(state->mName, shadow_id);
}
else
{
LL_DEBUGS("AOEngine") << "No state " << index << " or no animations defined for this state" << LL_ENDL;
}
}
currentState.insert("CurrentStateAnimations", currentAnimations);
LL_DEBUGS("AOEngine") << "Stored AO state: " << currentState << LL_ENDL;
gSavedPerAccountSettings.setLLSD("FSCurrentAOState", currentState);
clear(false); clear(false);
if (mRegionChangeConnection.connected()) if (mRegionChangeConnection.connected())
@ -458,11 +481,9 @@ void AOEngine::enable(bool enable)
// stop all overriders, catch leftovers // stop all overriders, catch leftovers
for (S32 index = 0; index < AOSet::AOSTATES_MAX; ++index) for (S32 index = 0; index < AOSet::AOSTATES_MAX; ++index)
{ {
state = mCurrentSet->getState(index); if (auto state = mCurrentSet->getState(index))
if (state)
{ {
LLUUID animation = state->mCurrentAnimationID; if (LLUUID animation = state->mCurrentAnimationID; animation.notNull())
if (animation.notNull())
{ {
LL_DEBUGS("AOEngine") << "Stopping leftover animation from state " << state->mName << LL_ENDL; LL_DEBUGS("AOEngine") << "Stopping leftover animation from state " << state->mName << LL_ENDL;
gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP); gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
@ -488,7 +509,7 @@ void AOEngine::enable(bool enable)
void AOEngine::setStateCycleTimer(const AOSet::AOState* state) void AOEngine::setStateCycleTimer(const AOSet::AOState* state)
{ {
F32 timeout = (F32)state->mCycleTime; F32 timeout = state->mCycleTime;
LL_DEBUGS("AOEngine") << "Setting cycle timeout for state " << state->mName << " of " << timeout << LL_ENDL; LL_DEBUGS("AOEngine") << "Setting cycle timeout for state " << state->mName << " of " << timeout << LL_ENDL;
if (timeout > 0.0f) if (timeout > 0.0f)
{ {
@ -615,8 +636,7 @@ const LLUUID AOEngine::override(const LLUUID& motion, bool start)
while ((stateNum = cleanupStates[index]) != AOSet::AOSTATES_MAX) while ((stateNum = cleanupStates[index]) != AOSet::AOSTATES_MAX)
{ {
// check if the next state is the one we are currently animating and skip that // check if the next state is the one we are currently animating and skip that
AOSet::AOState* stateToCheck = mCurrentSet->getState(stateNum); if (AOSet::AOState* stateToCheck = mCurrentSet->getState(stateNum); stateToCheck != state)
if (stateToCheck != state)
{ {
// check if there is an animation left over for that state // check if there is an animation left over for that state
if (!stateToCheck->mCurrentAnimationID.isNull()) if (!stateToCheck->mCurrentAnimationID.isNull())
@ -803,12 +823,12 @@ const LLUUID AOEngine::override(const LLUUID& motion, bool start)
void AOEngine::checkSitCancel() void AOEngine::checkSitCancel()
{ {
if (foreignAnimations()) if (!foreignAnimations())
return;
if (AOSet::AOState* aoState = mCurrentSet->getStateByRemapID(ANIM_AGENT_SIT))
{ {
if (AOSet::AOState* aoState = mCurrentSet->getStateByRemapID(ANIM_AGENT_SIT); aoState) if (LLUUID animation = aoState->mCurrentAnimationID; animation.notNull())
{
LLUUID animation = aoState->mCurrentAnimationID;
if (animation.notNull())
{ {
LL_DEBUGS("AOEngine") << "Stopping sit animation due to foreign animations running" << LL_ENDL; LL_DEBUGS("AOEngine") << "Stopping sit animation due to foreign animations running" << LL_ENDL;
gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP); gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
@ -821,7 +841,6 @@ void AOEngine::checkSitCancel()
} }
} }
} }
}
void AOEngine::cycleTimeout(const AOSet* set) void AOEngine::cycleTimeout(const AOSet* set)
{ {
@ -992,14 +1011,13 @@ void AOEngine::playAnimation(const LLUUID& animation)
return; return;
} }
if (!state->mAnimations.size()) if (state->mAnimations.empty())
{ {
LL_DEBUGS("AOEngine") << "cycle without animations in state." << LL_ENDL; LL_DEBUGS("AOEngine") << "cycle without animations in state." << LL_ENDL;
return; return;
} }
LLViewerInventoryItem* item = gInventory.getItem(animation); LLViewerInventoryItem* item = gInventory.getItem(animation);
if (!item) if (!item)
{ {
LL_WARNS("AOEngine") << "Inventory item for animation " << animation << " not found." << LL_ENDL; LL_WARNS("AOEngine") << "Inventory item for animation " << animation << " not found." << LL_ENDL;
@ -1007,10 +1025,10 @@ void AOEngine::playAnimation(const LLUUID& animation)
} }
AOSet::AOAnimation anim; AOSet::AOAnimation anim;
anim.mName = item->LLInventoryItem::getName(); anim.mName = item->getName();
anim.mInventoryUUID = item->getUUID(); anim.mInventoryUUID = item->getUUID();
anim.mOriginalUUID = item->getLinkedUUID(); anim.mOriginalUUID = item->getLinkedUUID();
anim.mAssetUUID = LLUUID::null; anim.mAssetUUID.setNull();
// if we can find the original animation already right here, save its asset ID, otherwise this will // if we can find the original animation already right here, save its asset ID, otherwise this will
// be tried again in AOSet::getAnimationForState() and/or AOEngine::cycle() // be tried again in AOSet::getAnimationForState() and/or AOEngine::cycle()
@ -1111,8 +1129,8 @@ void AOEngine::updateSortOrder(AOSet::AOState* state)
LL_WARNS("AOEngine") << "NULL inventory item found while trying to copy " << state->mAnimations[index].mInventoryUUID << LL_ENDL; LL_WARNS("AOEngine") << "NULL inventory item found while trying to copy " << state->mAnimations[index].mInventoryUUID << LL_ENDL;
continue; continue;
} }
LLPointer<LLViewerInventoryItem> newItem = new LLViewerInventoryItem(item);
LLPointer<LLViewerInventoryItem> newItem = new LLViewerInventoryItem(item);
newItem->setDescription(numStr.str()); newItem->setDescription(numStr.str());
newItem->setComplete(true); newItem->setComplete(true);
newItem->updateServer(false); newItem->updateServer(false);
@ -1440,7 +1458,7 @@ bool AOEngine::swapWithNext(AOSet::AOState* state, S32 index)
return true; return true;
} }
void AOEngine::reloadStateAnimations(AOSet::AOState* state) void AOEngine::reloadStateAnimations(AOSet* set, AOSet::AOState* state)
{ {
LLInventoryModel::item_array_t* items; LLInventoryModel::item_array_t* items;
LLInventoryModel::cat_array_t* dummy; LLInventoryModel::cat_array_t* dummy;
@ -1458,11 +1476,10 @@ void AOEngine::reloadStateAnimations(AOSet::AOState* state)
<< " asset " << item->getAssetUUID() << LL_ENDL; << " asset " << item->getAssetUUID() << LL_ENDL;
AOSet::AOAnimation anim; AOSet::AOAnimation anim;
anim.mName = item->LLInventoryItem::getName(); anim.mName = item->getName();
anim.mInventoryUUID = item->getUUID(); anim.mInventoryUUID = item->getUUID();
anim.mOriginalUUID = item->getLinkedUUID(); anim.mOriginalUUID = item->getLinkedUUID();
anim.mAssetUUID.setNull();
anim.mAssetUUID = LLUUID::null;
// if we can find the original animation already right here, save its asset ID, otherwise this will // if we can find the original animation already right here, save its asset ID, otherwise this will
// be tried again in AOSet::getAnimationForState() and/or AOEngine::cycle() // be tried again in AOSet::getAnimationForState() and/or AOEngine::cycle()
@ -1509,6 +1526,24 @@ void AOEngine::reloadStateAnimations(AOSet::AOState* state)
} }
updateSortOrder(state); updateSortOrder(state);
if (auto currentState = gSavedPerAccountSettings.getLLSD("FSCurrentAOState");
currentState.has("CurrentSet") && currentState["CurrentSet"].asString() == set->getName() &&
currentState.has("CurrentStateAnimations") && currentState["CurrentStateAnimations"].has(state->mName))
{
auto currentStateAnimId{ currentState["CurrentStateAnimations"][state->mName].asUUID() };
LLXORCipher cipher(ENCRYPTION_MAGIC_ID.mData, UUID_BYTES);
cipher.decrypt(currentStateAnimId.mData, UUID_BYTES);
for (const auto& animation : state->mAnimations)
{
if (animation.mAssetUUID == currentStateAnimId)
{
state->mCurrentAnimation = animation.mSortOrder;
break;
}
}
}
} }
void AOEngine::update() void AOEngine::update()
@ -1568,9 +1603,10 @@ void AOEngine::update()
newSet->setName(setName); newSet->setName(setName);
mSets.emplace_back(newSet); mSets.emplace_back(newSet);
if (auto currentSetName = gSavedPerAccountSettings.getString("FSCurrentAOSet"); currentSetName == setName) if (auto currentState = gSavedPerAccountSettings.getLLSD("FSCurrentAOState");
currentState.has("CurrentSet") && currentState["CurrentSet"].asString() == setName)
{ {
LL_DEBUGS("AOEngine") << "Selecting current set from settings: " << currentSetName << LL_ENDL; LL_DEBUGS("AOEngine") << "Selecting current set from settings: " << setName << LL_ENDL;
mCurrentSet = newSet; mCurrentSet = newSet;
} }
} }
@ -1677,7 +1713,7 @@ void AOEngine::update()
newSet->setComplete(false); newSet->setComplete(false);
continue; continue;
} }
reloadStateAnimations(state); reloadStateAnimations(newSet, state);
} }
} }
else else
@ -1772,7 +1808,6 @@ void AOEngine::selectSet(AOSet* set)
} }
mCurrentSet = set; mCurrentSet = set;
gSavedPerAccountSettings.setString("FSCurrentAOSet", mCurrentSet->getName());
if (mEnabled) if (mEnabled)
{ {
@ -2185,8 +2220,7 @@ void AOEngine::onNotecardLoadComplete(const LLUUID& assetUUID, LLAssetType::ETyp
char* buffer = new char[notecardSize + 1]; char* buffer = new char[notecardSize + 1];
buffer[notecardSize] = 0; buffer[notecardSize] = 0;
bool ret = file.read((U8*)buffer, notecardSize); if (file.read((U8*)buffer, notecardSize))
if (ret)
{ {
AOEngine::instance().parseNotecard(buffer); AOEngine::instance().parseNotecard(buffer);
} }
@ -2487,6 +2521,11 @@ void AOEngine::onRegionChange()
gAgent.sendAnimationRequest(mLastMotion, ANIM_REQUEST_START); gAgent.sendAnimationRequest(mLastMotion, ANIM_REQUEST_START);
} }
void AOEngine::updatePersistedStateAnimations()
{
}
// ---------------------------------------------------- // ----------------------------------------------------
AOSitCancelTimer::AOSitCancelTimer() AOSitCancelTimer::AOSitCancelTimer()
@ -2496,10 +2535,6 @@ AOSitCancelTimer::AOSitCancelTimer()
mEventTimer.stop(); mEventTimer.stop();
} }
AOSitCancelTimer::~AOSitCancelTimer()
{
}
void AOSitCancelTimer::oneShot() void AOSitCancelTimer::oneShot()
{ {
mTickCount = 0; mTickCount = 0;
@ -2534,10 +2569,6 @@ AOTimerCollection::AOTimerCollection()
updateTimers(); updateTimers();
} }
AOTimerCollection::~AOTimerCollection()
{
}
bool AOTimerCollection::tick() bool AOTimerCollection::tick()
{ {
if (mInventoryTimer) if (mInventoryTimer)

View File

@ -39,7 +39,7 @@ class AOTimerCollection
{ {
public: public:
AOTimerCollection(); AOTimerCollection();
~AOTimerCollection(); ~AOTimerCollection() = default;
virtual bool tick(); virtual bool tick();
@ -64,7 +64,7 @@ class AOSitCancelTimer
{ {
public: public:
AOSitCancelTimer(); AOSitCancelTimer();
~AOSitCancelTimer(); ~AOSitCancelTimer() = default;
void oneShot(); void oneShot();
void stop(); void stop();
@ -101,7 +101,7 @@ class AOEngine
void tick(); void tick();
void update(); void update();
void reload(bool); void reload(bool);
void reloadStateAnimations(AOSet::AOState* state); void reloadStateAnimations(AOSet* set, AOSet::AOState* state);
void clear(bool from_timer); void clear(bool from_timer);
const LLUUID& getAOFolder() const; const LLUUID& getAOFolder() const;
@ -196,6 +196,8 @@ class AOEngine
void* userdata, S32 status, LLExtStat extStatus); void* userdata, S32 status, LLExtStat extStatus);
void parseNotecard(const char* buffer); void parseNotecard(const char* buffer);
void updatePersistedStateAnimations();
updated_signal_t mUpdatedSignal; updated_signal_t mUpdatedSignal;
animation_changed_signal_t mAnimationChangedSignal; animation_changed_signal_t mAnimationChangedSignal;

View File

@ -171,8 +171,7 @@ const LLUUID& AOSet::getAnimationForState(AOState* state) const
{ {
if (state) if (state)
{ {
auto numOfAnimations = state->mAnimations.size(); if (auto numOfAnimations = state->mAnimations.size(); numOfAnimations > 0)
if (numOfAnimations)
{ {
if (state->mCycle) if (state->mCycle)
{ {
@ -198,7 +197,7 @@ const LLUUID& AOSet::getAnimationForState(AOState* state) const
{ {
LL_DEBUGS("AOEngine") << "Asset UUID for chosen animation " << anim.mName << " not yet known, try to find it." << LL_ENDL; LL_DEBUGS("AOEngine") << "Asset UUID for chosen animation " << anim.mName << " not yet known, try to find it." << LL_ENDL;
if(LLViewerInventoryItem* item = gInventory.getItem(anim.mInventoryUUID) ; item) if (LLViewerInventoryItem* item = gInventory.getItem(anim.mInventoryUUID))
{ {
LL_DEBUGS("AOEngine") << "Found asset UUID for chosen animation: " << item->getAssetUUID() << " - Updating AOAnimation.mAssetUUID" << LL_ENDL; LL_DEBUGS("AOEngine") << "Found asset UUID for chosen animation: " << item->getAssetUUID() << " - Updating AOAnimation.mAssetUUID" << LL_ENDL;
anim.mAssetUUID = item->getAssetUUID(); anim.mAssetUUID = item->getAssetUUID();

View File

@ -1517,16 +1517,16 @@
<key>Value</key> <key>Value</key>
<integer>1</integer> <integer>1</integer>
</map> </map>
<key>FSCurrentAOSet</key> <key>FSCurrentAOState</key>
<map> <map>
<key>Comment</key> <key>Comment</key>
<string>The currently active AO set</string> <string>The currently active AO set and animation per state</string>
<key>Persist</key> <key>Persist</key>
<integer>1</integer> <integer>1</integer>
<key>Type</key> <key>Type</key>
<string>String</string> <string>LLSD</string>
<key>Value</key> <key>Value</key>
<string/> <map/>
</map> </map>
</map> </map>
</llsd> </llsd>

View File

@ -292,10 +292,10 @@ using namespace LL;
#include "nd/ndetw.h" // <FS:ND/> Windows Event Tracing, does nothing on OSX/Linux. #include "nd/ndetw.h" // <FS:ND/> Windows Event Tracing, does nothing on OSX/Linux.
#include "nd/ndlogthrottle.h" #include "nd/ndlogthrottle.h"
#include "aoengine.h"
#include "fsradar.h" #include "fsradar.h"
#include "fsassetblacklist.h" #include "fsassetblacklist.h"
#include "bugsplatattributes.h" #include "bugsplatattributes.h"
// #include "fstelemetry.h" // <FS:Beq> Tracy profiler support
#if LL_LINUX && LL_GTK #if LL_LINUX && LL_GTK
#include "glib.h" #include "glib.h"
@ -2270,6 +2270,8 @@ bool LLAppViewer::cleanup()
LLEnvironment::getInstance()->saveToSettings(); LLEnvironment::getInstance()->saveToSettings();
} }
AOEngine::deleteSingleton();
// Must do this after all panels have been deleted because panels that have persistent rects // Must do this after all panels have been deleted because panels that have persistent rects
// save their rects on delete. // save their rects on delete.
if(mSaveSettingsOnExit) // <FS:Zi> Backup Settings if(mSaveSettingsOnExit) // <FS:Zi> Backup Settings