diff --git a/indra/llfilesystem/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt index 9f24f75eabc2116e50e2674983543ceb1e44aeb1..d14f2adb19be7b5ec4d1e519915caaee2183aabc 100644 --- a/indra/llfilesystem/CMakeLists.txt +++ b/indra/llfilesystem/CMakeLists.txt @@ -56,6 +56,10 @@ target_link_libraries(llfilesystem ) target_include_directories( llfilesystem INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +if(TARGET al::sentry) + target_compile_definitions(llfilesystem PRIVATE AL_SENTRY=1) +endif() + # Add tests if (LL_TESTS) include(LLAddBuildTest) diff --git a/indra/llfilesystem/lldir_linux.cpp b/indra/llfilesystem/lldir_linux.cpp index 93f2962dcc318ba99d8f0e4694c9ac37a85180fc..2041a091c54a756992b38300ab294b9597d9df36 100644 --- a/indra/llfilesystem/lldir_linux.cpp +++ b/indra/llfilesystem/lldir_linux.cpp @@ -88,21 +88,25 @@ LLDir_Linux::LLDir_Linux() #ifdef APP_RO_DATA_DIR mAppRODataDir = APP_RO_DATA_DIR; #else - mAppRODataDir = tmp_str; -#endif - std::string::size_type build_dir_pos = mExecutableDir.rfind("/build-linux-"); + // Determine the location of the App-Read-Only-Data + // Try the working directory then the exe's dir. +#ifndef AL_SENTRY + std::string::size_type build_dir_pos = mExecutableDir.rfind(mDirDelimiter + "build-linux-"); if (build_dir_pos != std::string::npos) { // ...we're in a dev checkout - mSkinBaseDir = mExecutableDir.substr(0, build_dir_pos) + "/indra/newview/skins"; - LL_INFOS() << "Running in dev checkout with mSkinBaseDir " - << mSkinBaseDir << LL_ENDL; + mAppRODataDir = add(mExecutableDir.substr(0, build_dir_pos), "indra", "newview"); + LL_INFOS() << "Running in dev checkout with mAppRODataDir " << mAppRODataDir << LL_ENDL; } else +#endif { // ...normal installation running - mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins"; + mAppRODataDir = tmp_str; } +#endif + + mSkinBaseDir = add(mAppRODataDir, "skins"); mOSUserDir = getCurrentUserHome(tmp_str); mOSUserAppDir = ""; diff --git a/indra/llfilesystem/lldir_win32.cpp b/indra/llfilesystem/lldir_win32.cpp index 615abb30f442f95435c63e2b4374b22309109d86..faf7d5147dbc6962685ef35a092b444124332d98 100644 --- a/indra/llfilesystem/lldir_win32.cpp +++ b/indra/llfilesystem/lldir_win32.cpp @@ -246,7 +246,7 @@ LLDir_Win32::LLDir_Win32() } else { - fprintf(stderr, "Couldn't get APP path, assuming current directory!"); + PRELOG("Couldn't get APP path, assuming current directory!"); mExecutableDir = mWorkingDir; // Assume it's the current directory } @@ -255,8 +255,20 @@ LLDir_Win32::LLDir_Win32() // Determine the location of the App-Read-Only-Data // Try the working directory then the exe's dir. - mAppRODataDir = mWorkingDir; - +#ifndef AL_SENTRY + std::string::size_type build_dir_pos = mExecutableDir.rfind(mDirDelimiter + "build-vc-"); + if (build_dir_pos != std::string::npos) + { + // ...we're in a dev checkout + mAppRODataDir = add(mExecutableDir.substr(0, build_dir_pos), "indra", "newview"); + PRELOG("Running in dev checkout with mAppRODataDir " << mAppRODataDir); + } + else +#endif + { + // ...normal installation running + mAppRODataDir = mWorkingDir; + } // if (mExecutableDir.find("indra") == std::string::npos) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 6a75d3494c8aa358a3d4e13746141672e9d93362..ae69a490066c0c9a5b5ba471046ec1f949decb9c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -400,6 +400,7 @@ set(viewer_SOURCE_FILES llhudeffectpointat.cpp llhudeffecttrail.cpp llhudeffectblob.cpp + llhudeffectresetskeleton.cpp llhudicon.cpp llhudmanager.cpp llhudnametag.cpp @@ -1148,6 +1149,7 @@ set(viewer_HEADER_FILES llhudeffectpointat.h llhudeffecttrail.h llhudeffectblob.h + llhudeffectresetskeleton.h llhudicon.h llhudmanager.h llhudnametag.h diff --git a/indra/newview/app_settings/colorlut/the_perfect_lut.png b/indra/newview/app_settings/colorlut/the_perfect_lut.png new file mode 100644 index 0000000000000000000000000000000000000000..f8513cd758a7da9e522ab2c0c15dac351aa2572e Binary files /dev/null and b/indra/newview/app_settings/colorlut/the_perfect_lut.png differ diff --git a/indra/newview/llhudeffectresetskeleton.cpp b/indra/newview/llhudeffectresetskeleton.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8afa231fc53093f1490c7e6849c2e97e40caefa2 --- /dev/null +++ b/indra/newview/llhudeffectresetskeleton.cpp @@ -0,0 +1,207 @@ +/** + * @file llhudeffectresetskeleton.cpp + * @brief LLHUDEffectResetSkeleton class implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llhudeffectresetskeleton.h" + +#include "llagent.h" +#include "llviewerobjectlist.h" +#include "llvoavatar.h" +#include "message.h" + +// packet layout +const S32 TARGET_OBJECT = 0; // This is to allow for targetting owned animesh +const S32 RESET_ANIMATIONS = 16; //This can also be a flags if needed +const S32 PKT_SIZE = 17; + +//----------------------------------------------------------------------------- +// LLHUDEffectResetSkeleton() +//----------------------------------------------------------------------------- +LLHUDEffectResetSkeleton::LLHUDEffectResetSkeleton(const U8 type) : + LLHUDEffect(type) +{ +} + +//----------------------------------------------------------------------------- +// ~LLHUDEffectResetSkeleton() +//----------------------------------------------------------------------------- +LLHUDEffectResetSkeleton::~LLHUDEffectResetSkeleton() +{ +} + +//----------------------------------------------------------------------------- +// packData() +//----------------------------------------------------------------------------- +void LLHUDEffectResetSkeleton::packData(LLMessageSystem *mesgsys) +{ + // Pack the default data + LLHUDEffect::packData(mesgsys); + + // Pack the type-specific data. Uses a fun packed binary format. Whee! + U8 packed_data[PKT_SIZE]; + memset(packed_data, 0, PKT_SIZE); + + // pack both target object and position + // position interpreted as offset if target object is non-null + if (mTargetObject) + { + htolememcpy(&(packed_data[TARGET_OBJECT]), mTargetObject->mID.mData, MVT_LLUUID, 16); + } + else + { + htolememcpy(&(packed_data[TARGET_OBJECT]), LLUUID::null.mData, MVT_LLUUID, 16); + } + + U8 resetAnimations = (U8)mResetAnimations; + htolememcpy(&(packed_data[RESET_ANIMATIONS]), &resetAnimations, MVT_U8, 1); + + mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, PKT_SIZE); +} + +//----------------------------------------------------------------------------- +// unpackData() +//----------------------------------------------------------------------------- +void LLHUDEffectResetSkeleton::unpackData(LLMessageSystem *mesgsys, S32 blocknum) +{ + LLVector3d new_target; + U8 packed_data[PKT_SIZE]; + + + LLHUDEffect::unpackData(mesgsys, blocknum); + + LLUUID source_id; + mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_AgentID, source_id, blocknum); + + LLViewerObject *objp = gObjectList.findObject(source_id); + if (objp && objp->isAvatar()) + { + setSourceObject(objp); + } + else + { + //LL_WARNS() << "Could not find source avatar for ResetSkeleton effect" << LL_ENDL; + return; + } + + S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData); + if (size != PKT_SIZE) + { + LL_WARNS() << "ResetSkeleton effect with bad size " << size << LL_ENDL; + return; + } + + mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, PKT_SIZE, blocknum); + + LLUUID target_id; + htolememcpy(target_id.mData, &(packed_data[TARGET_OBJECT]), MVT_LLUUID, 16); + + if (target_id.isNull()) + { + target_id = source_id; + } + + objp = gObjectList.findObject(target_id); + + if (objp) + { + setTargetObject(objp); + } + + U8 resetAnimations = 0; + htolememcpy(&resetAnimations, &(packed_data[RESET_ANIMATIONS]), MVT_U8, 1); + + // Pre-emptively assume this is going to be flags in the future. + // It isn't needed now, but this will assure that only bit 1 is set + mResetAnimations = resetAnimations & 1; + + update(); +} + +//----------------------------------------------------------------------------- +// setTargetObjectAndOffset() +//----------------------------------------------------------------------------- +void LLHUDEffectResetSkeleton::setTargetObject(LLViewerObject *objp) +{ + mTargetObject = objp; +} + + +//----------------------------------------------------------------------------- +// markDead() +//----------------------------------------------------------------------------- +void LLHUDEffectResetSkeleton::markDead() +{ + LLHUDEffect::markDead(); +} + +void LLHUDEffectResetSkeleton::setSourceObject(LLViewerObject* objectp) +{ + // restrict source objects to avatars + if (objectp && objectp->isAvatar()) + { + LLHUDEffect::setSourceObject(objectp); + } +} + +//----------------------------------------------------------------------------- +// update() +//----------------------------------------------------------------------------- +void LLHUDEffectResetSkeleton::update() +{ + // If the target object is dead, set the target object to NULL + if (mTargetObject.isNull() || mTargetObject->isDead()) + { + markDead(); + return; + } + + if (mSourceObject.isNull() || mSourceObject->isDead()) + { + markDead(); + return; + } + + bool owned = false; + if(mTargetObject->isAnimatedObject()) + { + owned = mTargetObject->mOwnerID == mSourceObject->getID(); + } + else + { + owned = mTargetObject->getID() == mSourceObject->getID(); + } + + if (owned) + { + if (mTargetObject->isAvatar() || mTargetObject->isAnimatedObject()) + { + ((LLVOAvatar*)(LLViewerObject*)mTargetObject)->resetSkeleton(mResetAnimations); + } + } + + markDead(); +} diff --git a/indra/newview/llhudeffectresetskeleton.h b/indra/newview/llhudeffectresetskeleton.h new file mode 100644 index 0000000000000000000000000000000000000000..ddac84165f39ec24521b1f8dc17e09920c24cd9e --- /dev/null +++ b/indra/newview/llhudeffectresetskeleton.h @@ -0,0 +1,59 @@ +/** + * @file llhudeffectresetskeleton.h + * @brief LLHUDEffectResetSkeleton class definition + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#ifndef LL_LLHUDEFFECTRESETSKELETON_H +#define LL_LLHUDEFFECTRESETSKELETON_H + +#include "llhudeffect.h" + +class LLViewerObject; +class LLVOAvatar; + + +class LLHUDEffectResetSkeleton final : public LLHUDEffect +{ +public: + friend class LLHUDObject; + + /*virtual*/ void markDead(); + /*virtual*/ void setSourceObject(LLViewerObject* objectp); + + void setTargetObject(LLViewerObject *objp); + void setResetAnimations(bool enable){ mResetAnimations = enable; }; + +protected: + LLHUDEffectResetSkeleton(const U8 type); + ~LLHUDEffectResetSkeleton(); + + /*virtual*/ void packData(LLMessageSystem *mesgsys); + /*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum); + + void update(); +private: + bool mResetAnimations; +}; + +#endif // LL_LLHUDEFFECTRESETSKELETON_H diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp index 36a28b826961422730e49f225b6c23a37c901429..590ea3b34ec1e5cd473ce7585935fceec2ce8a5a 100644 --- a/indra/newview/llhudobject.cpp +++ b/indra/newview/llhudobject.cpp @@ -36,6 +36,7 @@ #include "llhudeffecttrail.h" #include "llhudeffectlookat.h" #include "llhudeffectpointat.h" +#include "llhudeffectresetskeleton.h" #include "llhudnametag.h" #include "llvoicevisualizer.h" @@ -241,6 +242,9 @@ LLHUDEffect *LLHUDObject::addHUDEffect(const U8 type) case LL_HUD_EFFECT_BLOB: hud_objectp = new LLHUDEffectBlob(type); break; + case LL_HUD_EFFECT_RESET_SKELETON: + hud_objectp = new LLHUDEffectResetSkeleton(type); + break; default: LL_WARNS() << "Unknown type of hud effect:" << (U32) type << LL_ENDL; } diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h index 68149daceae9043f63c2061528fb4a634091f35c..9c4eaf3d69d56e69b825377962dc1dacc0500bc7 100644 --- a/indra/newview/llhudobject.h +++ b/indra/newview/llhudobject.h @@ -100,7 +100,8 @@ class LLHUDObject : public LLRefCount LL_HUD_EFFECT_POINTAT, LL_HUD_EFFECT_VOICE_VISUALIZER, // Ventrella LL_HUD_NAME_TAG, - LL_HUD_EFFECT_BLOB + LL_HUD_EFFECT_BLOB, + LL_HUD_EFFECT_RESET_SKELETON }; protected: static void sortObjects(); diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 20501f17e54b9233e7d4bb33fd4721b569a44f38..6d80214ed780ac1dda909c342b3f3ff2e0e2f07e 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -3253,7 +3253,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root for (LLInventoryModel::item_array_t::value_type& item : items) { - if (get_is_item_worn(item)) + if (!item->getIsLinkType() && get_is_item_worn(item)) { has_worn = true; LLWearableType::EType type = item->getWearableType(); @@ -3273,7 +3273,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root } } LLViewerInventoryItem* item = gInventory.getItem(obj_id); - if (item && get_is_item_worn(item)) + if (item && !item->getIsLinkType() && get_is_item_worn(item)) { has_worn = true; LLWearableType::EType type = item->getWearableType(); diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index 4dae97088f194dee43ca41865799aad6e4c6f85b..cc7afdfd332cbf3fa4e91c9567434bca85c85ca7 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -1988,7 +1988,7 @@ void LLInventoryGallery::deleteSelection() for (LLInventoryModel::item_array_t::value_type& item : items) { - if (get_is_item_worn(item)) + if (!item->getIsLinkType() && get_is_item_worn(item)) { has_worn = true; LLWearableType::EType type = item->getWearableType(); @@ -2009,7 +2009,7 @@ void LLInventoryGallery::deleteSelection() } LLViewerInventoryItem* item = gInventory.getItem(id); - if (item && get_is_item_worn(item)) + if (item && !item->getIsLinkType() && get_is_item_worn(item)) { has_worn = true; LLWearableType::EType type = item->getWearableType(); diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index 3bd0fc098ce9f8b13c125436ddfce3be7ceb801e..79dbde6225d2e0d7e1f15ff79f3b649b24b79f9c 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -239,11 +239,11 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata) } else if ("copy_slurl" == action) { - boost::function<void(LLLandmark*)> copy_slurl_cb = [](LLLandmark* landmark) + std::function<void(LLLandmark*)> copy_slurl_cb = [](LLLandmark* landmark) { LLVector3d global_pos; landmark->getGlobalPos(global_pos); - boost::function<void(std::string& slurl)> copy_slurl_to_clipboard_cb = [](std::string& slurl) + std::function<void(std::string& slurl)> copy_slurl_to_clipboard_cb = [](std::string& slurl) { gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl)); LLSD args; diff --git a/indra/newview/lllandmarkactions.h b/indra/newview/lllandmarkactions.h index 29ffc1dcee4646823a5c20a82a72cca4aa75825c..b19132e1a3521a2a4bddb17385c286094f4a249c 100644 --- a/indra/newview/lllandmarkactions.h +++ b/indra/newview/lllandmarkactions.h @@ -39,8 +39,8 @@ class LLLandmark; class LLLandmarkActions { public: - typedef boost::function<void(std::string& slurl)> slurl_callback_t; - typedef boost::function<void(std::string& slurl, S32 x, S32 y, S32 z)> region_name_and_coords_callback_t; + typedef std::function<void(std::string& slurl)> slurl_callback_t; + typedef std::function<void(std::string& slurl, S32 x, S32 y, S32 z)> region_name_and_coords_callback_t; /** * @brief Fetches landmark LLViewerInventoryItems for the given landmark name. diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index a2b431b890bede1f1f2169771d1e4b2987e3418e..e445cb31925d0ddf8e8db8c247b66d8eedf25204 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -590,6 +590,19 @@ bool idle_startup() { LLMessageConfig::initClass("viewer", gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); } + #elif LL_LINUX + // On the windows dev builds, unpackaged, the message.xml file will + // be located in indra/build-vc**/newview/<config>/app_settings. + std::string message_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "message.xml"); + + if (!LLFile::isfile(message_path)) + { + LLMessageConfig::initClass("viewer", gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, ".." ,"app_settings", "")); + } + else + { + LLMessageConfig::initClass("viewer", gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + } #else LLMessageConfig::initClass("viewer", gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); #endif diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 4da296410d31dc0aa5ef923b52d1e7cbb00e5076..858bc5b4246c6afa6018a720955d3c5b39f4b1ae 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -92,6 +92,7 @@ #include "lltoolface.h" #include "llhints.h" #include "llhudeffecttrail.h" +#include "llhudeffectresetskeleton.h" #include "llhudmanager.h" #include "llimview.h" #include "llinventorybridge.h" @@ -6797,10 +6798,19 @@ class LLAvatarResetSkeleton: public view_listener_t LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); if(avatar) { - avatar->resetSkeleton(false); -// [SL:KB] - Patch: Appearance-RefreshAttachments | Checked: Catznip-5.3 - avatar->rebuildAttachments(); -// [/SL:KB] + bool owned = false; + if(avatar->isAnimatedObject()) + { + owned = avatar->mOwnerID == gAgent.getID(); + } + else + { + owned = avatar->getID() == gAgent.getID(); + } + LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, owned); + effectp->setSourceObject(gAgentAvatarp); + effectp->setTargetObject((LLViewerObject*)avatar); + effectp->setResetAnimations(false); } return true; } @@ -6827,10 +6837,19 @@ class LLAvatarResetSkeletonAndAnimations : public view_listener_t LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); if (avatar) { - avatar->resetSkeleton(true); -// [SL:KB] - Patch: Appearance-RefreshAttachments | Checked: Catznip-5.3 - avatar->rebuildAttachments(); -// [/SL:KB] + bool owned = false; + if(avatar->isAnimatedObject()) + { + owned = avatar->mOwnerID == gAgent.getID(); + } + else + { + owned = avatar->getID() == gAgent.getID(); + } + LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, owned); + effectp->setSourceObject(gAgentAvatarp); + effectp->setTargetObject((LLViewerObject*)avatar); + effectp->setResetAnimations(true); } return true; } @@ -6843,11 +6862,26 @@ class LLAvatarResetSelfSkeletonAndAnimations : public view_listener_t LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); if (avatar) { - avatar->resetSkeleton(true); + bool owned = false; + if(avatar->isAnimatedObject()) + { + owned = avatar->mOwnerID == gAgent.getID(); + } + else + { + owned = avatar->getID() == gAgent.getID(); + } + LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, owned); + effectp->setSourceObject(gAgentAvatarp); + effectp->setTargetObject((LLViewerObject*)avatar); + effectp->setResetAnimations(true); } else { - gAgentAvatarp->resetSkeleton(true); + LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, true); + effectp->setSourceObject(gAgentAvatarp); + effectp->setTargetObject(gAgentAvatarp); + effectp->setResetAnimations(true); } return true; } diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 326ebe7ed447a725fcbb17febb9813741794dc0b..c17dbcf4ead2b3ac999b43cb67202f646986ebbe 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -3090,24 +3090,33 @@ void LLViewerObject::fetchInventoryFromServer() delete mInventory; mInventory = NULL; - // Results in processTaskInv - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RequestTaskInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addU32Fast(_PREHASH_LocalID, mLocalID); - msg->sendReliable(mRegionp->getHost()); - // This will get reset by doInventoryCallback or processTaskInv mInvRequestState = INVENTORY_REQUEST_PENDING; + + if (mRegionp && !mRegionp->getCapability("RequestTaskInventory").empty()) + { + LLCoros::instance().launch("LLViewerObject::fetchInventoryFromCapCoro()", + boost::bind(&LLViewerObject::fetchInventoryFromCapCoro, mID)); + } + else + { + LL_WARNS() << "Using old task inventory path!" << LL_ENDL; + // Results in processTaskInv + LLMessageSystem *msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RequestTaskInventory); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addU32Fast(_PREHASH_LocalID, mLocalID); + msg->sendReliable(mRegionp->getHost()); + } } } void LLViewerObject::fetchInventoryDelayed(const F64 &time_seconds) { - // unless already waiting, drop previous request and shedule an update + // unless already waiting, drop previous request and schedule an update if (mInvRequestState != INVENTORY_REQUEST_WAIT) { if (mInvRequestXFerId != 0) @@ -3138,6 +3147,80 @@ void LLViewerObject::fetchInventoryDelayedCoro(const LLUUID task_inv, const F64 } } +//static +void LLViewerObject::fetchInventoryFromCapCoro(const LLUUID task_inv) +{ + LLViewerObject *obj = gObjectList.findObject(task_inv); + if (obj) + { + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TaskInventoryRequest", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + std::string url = obj->mRegionp->getCapability("RequestTaskInventory") + "?task_id=" + obj->mID.asString(); + // If we already have a copy of the inventory then add it so the server won't re-send something we already have. + // We expect this case to crop up in the case of failed inventory mutations, but it might happen otherwise as well. + if (obj->mInventorySerialNum && obj->mInventory) + url += "&inventory_serial=" + std::to_string(obj->mInventorySerialNum); + + obj->mInvRequestState = INVENTORY_XFER; + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + // Object may have gone away while we were suspended, double-check that it still exists + obj = gObjectList.findObject(task_inv); + if (!obj) + { + LL_WARNS() << "Object " << task_inv << " went away while fetching inventory, dropping result" << LL_ENDL; + return; + } + + bool potentially_stale = false; + if (status) + { + // Dealing with inventory serials is kind of funky. They're monotonically increasing and 16 bits, + // so we expect them to overflow, but we can use inv serial < expected serial as a signal that we may + // have mutated the task inventory since we kicked off the request, and those mutations may have not + // been taken into account yet. Of course, those mutations may have actually failed which would result + // in the inv serial never increasing. + // + // When we detect this case, set the expected inv serial to the inventory serial we actually received + // and kick off a re-request after a slight delay. + S16 serial = (S16)result["inventory_serial"].asInteger(); + potentially_stale = serial < obj->mExpectedInventorySerialNum; + LL_INFOS() << "Inventory loaded for " << task_inv << LL_ENDL; + obj->mInventorySerialNum = serial; + obj->mExpectedInventorySerialNum = serial; + obj->loadTaskInvLLSD(result); + } + else if (status.getType() == 304) + { + LL_INFOS() << "Inventory wasn't changed on server!" << LL_ENDL; + obj->mInvRequestState = INVENTORY_REQUEST_STOPPED; + // Even though it wasn't necessary to send a response, we still may have mutated + // the inventory since we kicked off the request, check for that case. + potentially_stale = obj->mInventorySerialNum < obj->mExpectedInventorySerialNum; + // Set this to what we already have so that we don't re-request a second time. + obj->mExpectedInventorySerialNum = obj->mInventorySerialNum; + } + else + { + // Not sure that there's anything sensible we can do to recover here, retrying in a loop would be bad. + LL_WARNS() << "Error status while requesting task inventory: " << status.toString() << LL_ENDL; + obj->mInvRequestState = INVENTORY_REQUEST_STOPPED; + } + + if (potentially_stale) + { + // Stale? I guess we can use what we got for now, but we'll have to re-request + LL_WARNS() << "Stale inv_serial? Re-requesting." << LL_ENDL; + obj->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_OUTDATED); + } + } +} + LLControlAvatar *LLViewerObject::getControlAvatar() { return getRootEdit()->mControlAvatar.get(); @@ -3322,6 +3405,20 @@ void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data) S16 serial = 0; msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, serial); + if (object->mRegionp && !object->mRegionp->getCapability("RequestTaskInventory").empty()) + { + // It seems that simulator may ask us to re-download the task inventory if an update to the inventory + // happened out-of-band while we had the object selected (like if a script is saved.) + // + // If we're meant to use the HTTP capability, ignore the contents of the UDP message and fetch the + // inventory via the CAP so that we don't flow down the UDP inventory request path unconditionally here. + // We shouldn't need to wait, as any updates should already be ready to fetch by this point. + LL_INFOS() << "Handling unsolicited ReplyTaskInventory for " << task_id << LL_ENDL; + object->mExpectedInventorySerialNum = serial; + object->fetchInventoryFromServer(); + return; + } + if (serial == object->mInventorySerialNum && serial < object->mExpectedInventorySerialNum) { @@ -3529,6 +3626,47 @@ BOOL LLViewerObject::loadTaskInvFile(const std::string& filename) return TRUE; } +void LLViewerObject::loadTaskInvLLSD(const LLSD& inv_result) +{ + if (inv_result.has("contents")) + { + if(mInventory) + { + mInventory->clear(); // will deref and delete it + } + else + { + mInventory = new LLInventoryObject::object_list_t; + } + + // Synthesize the "Contents" category, the viewer expects it, but it isn't sent. + LLPointer<LLInventoryObject> inv = new LLInventoryObject(mID, LLUUID::null, LLAssetType::AT_CATEGORY, "Contents"); + mInventory->push_front(inv); + + const LLSD& inventory = inv_result["contents"]; + for (const auto& inv_entry : llsd::inArray(inventory)) + { + if (inv_entry.has("item_id")) + { + LLPointer<LLViewerInventoryItem> inv = new LLViewerInventoryItem; + inv->unpackMessage(inv_entry); + mInventory->push_front(inv); + } + else + { + LL_WARNS_ONCE() << "Unknown inventory entry while reading from inventory file. Entry: '" + << inv_entry << "'" << LL_ENDL; + } + } + } + else + { + LL_WARNS() << "unable to load task inventory: " << inv_result << LL_ENDL; + return; + } + doInventoryCallback(); +} + void LLViewerObject::doInventoryCallback() { for (callback_list_t::iterator iter = mInventoryCallbacks.begin(); diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index c5176503eb964855db62f477709fa628616f8b2b..4e002560a28677ea2842fe63b368d00068b48a75 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -730,6 +730,7 @@ class LLViewerObject // forms task inventory request after some time passed, marks request as pending void fetchInventoryDelayed(const F64 &time_seconds); static void fetchInventoryDelayedCoro(const LLUUID task_inv, const F64 time_seconds); + static void fetchInventoryFromCapCoro(const LLUUID task_inv); public: // @@ -861,6 +862,7 @@ class LLViewerObject static void processTaskInvFile(void** user_data, S32 error_code, LLExtStat ext_status); BOOL loadTaskInvFile(const std::string& filename); + void loadTaskInvLLSD(const LLSD &inv_result); void doInventoryCallback(); BOOL isOnMap(); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 54f0ea3d5d8c4cdc7a90de3ea52d67ff40659348..4a8acf74cb7fd3dd08d513e2697b570d2eaaec7e 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3400,6 +3400,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("FetchInventory2"); capabilityNames.append("FetchInventoryDescendents2"); capabilityNames.append("IncrementCOFVersion"); + capabilityNames.append("RequestTaskInventory"); AISAPI::getCapNames(capabilityNames); } diff --git a/indra/newview/llworldmapmessage.h b/indra/newview/llworldmapmessage.h index e81aaae4e2c3f95d89054fd51955f9c937fdb602..d20259672579336d4e836fe5900bbe99df733876 100644 --- a/indra/newview/llworldmapmessage.h +++ b/indra/newview/llworldmapmessage.h @@ -38,7 +38,7 @@ class LLWorldMapMessage final : public LLSingleton<LLWorldMapMessage> ~LLWorldMapMessage(); public: - typedef boost::function<void(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport)> + typedef std::function<void(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport)> url_callback_t; // Process incoming answers to map stuff requests