diff options
29 files changed, 627 insertions, 34 deletions
@@ -498,3 +498,4 @@ bc61801f614022c920cb5c3df1d7d67a9561ce1f 3.7.22-release 3be800e1afad9615442159e388d6d137be7b951e 3.7.23-release d3d0101e980ec95043e0af9b7903045d3bc447e4 3.7.24-release 9978a8c3a2ffce4a5e1c186256581c2ac139c9dc 3.7.25-release +000e9dda4162cbf0a83ba88558b19473654a09a9 3.7.26-release diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h index d4e3b76386..1a3e307663 100755 --- a/indra/llcharacter/llcharacter.h +++ b/indra/llcharacter/llcharacter.h @@ -261,6 +261,9 @@ public: static std::vector< LLCharacter* > sInstances; static BOOL sAllowInstancesChange ; //debug use + virtual void setHoverOffset(const LLVector3& hover_offset, bool send_update=true) { mHoverOffset = hover_offset; } + const LLVector3& getHoverOffset() const { return mHoverOffset; } + protected: LLMotionController mMotionController; @@ -273,7 +276,6 @@ protected: U32 mSkeletonSerialNum; LLAnimPauseRequest mPauseRequest; - private: // visual parameter stuff typedef std::map<S32, LLVisualParam *> visual_param_index_map_t; @@ -284,6 +286,8 @@ private: visual_param_name_map_t mVisualParamNameMap; static LLStringTable sVisualParamNames; + + LLVector3 mHoverOffset; }; #endif // LL_LLCHARACTER_H diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp index 6f22a7c6b7..40695bfa24 100755 --- a/indra/llcharacter/lljoint.cpp +++ b/indra/llcharacter/lljoint.cpp @@ -290,7 +290,7 @@ const LLVector3& LLJoint::getPosition() bool do_debug_joint(const std::string& name) { - return true; + return false; } //-------------------------------------------------------------------- diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index 39cfb6019e..a62b9c3227 100755 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -1383,3 +1383,5 @@ char const* const _PREHASH_GroupAVSounds = LLMessageStringTable::getInstance()-> char const* const _PREHASH_AppearanceData = LLMessageStringTable::getInstance()->getString("AppearanceData"); char const* const _PREHASH_AppearanceVersion = LLMessageStringTable::getInstance()->getString("AppearanceVersion"); char const* const _PREHASH_CofVersion = LLMessageStringTable::getInstance()->getString("CofVersion"); +char const* const _PREHASH_AppearanceHover = LLMessageStringTable::getInstance()->getString("AppearanceHover"); +char const* const _PREHASH_HoverHeight = LLMessageStringTable::getInstance()->getString("HoverHeight"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 573e10dc0b..573c8e466f 100755 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -1383,4 +1383,6 @@ extern char const* const _PREHASH_GroupAVSounds; extern char const* const _PREHASH_AppearanceData; extern char const* const _PREHASH_AppearanceVersion; extern char const* const _PREHASH_CofVersion; +extern char const* const _PREHASH_AppearanceHover; +extern char const* const _PREHASH_HoverHeight; #endif diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index 62c5ecb8f1..2da39fa54b 100755 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -58,7 +58,8 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p) mPrecision(p.decimal_digits), mTextEnabledColor(p.text_color()), mTextDisabledColor(p.text_disabled_color()), - mLabelWidth(p.label_width) + mLabelWidth(p.label_width), + mEditorCommitSignal(NULL) { S32 top = getRect().getHeight(); S32 bottom = 0; @@ -196,6 +197,11 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p) updateText(); } +LLSliderCtrl::~LLSliderCtrl() +{ + delete mEditorCommitSignal; +} + // static void LLSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata ) { @@ -308,6 +314,8 @@ void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata ) if( success ) { self->onCommit(); + if (self->mEditorCommitSignal) + (*(self->mEditorCommitSignal))(self, self->getValueF32()); } else { @@ -421,6 +429,11 @@ boost::signals2::connection LLSliderCtrl::setSliderMouseUpCallback( const commit return mSlider->setMouseUpCallback( cb ); } +boost::signals2::connection LLSliderCtrl::setSliderEditorCommitCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mEditorCommitSignal) mEditorCommitSignal = new commit_signal_t(); + return mEditorCommitSignal->connect(cb); +} void LLSliderCtrl::onTabInto() { if( mEditor ) diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h index 5153e33f49..67cca9ef04 100755 --- a/indra/llui/llsliderctrl.h +++ b/indra/llui/llsliderctrl.h @@ -81,7 +81,7 @@ protected: LLSliderCtrl(const Params&); friend class LLUICtrlFactory; public: - virtual ~LLSliderCtrl() {} // Children all cleaned up by default view destructor. + virtual ~LLSliderCtrl(); /*virtual*/ F32 getValueF32() const { return mSlider->getValueF32(); } void setValue(F32 v, BOOL from_event = FALSE); @@ -112,6 +112,7 @@ public: boost::signals2::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb ); boost::signals2::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setSliderEditorCommitCallback( const commit_signal_t::slot_type& cb ); /*virtual*/ void onTabInto(); @@ -150,6 +151,8 @@ private: LLUIColor mTextEnabledColor; LLUIColor mTextDisabledColor; + + commit_signal_t* mEditorCommitSignal; }; #endif // LL_LLSLIDERCTRL_H diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 154f3b5726..318e3c8688 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -245,6 +245,7 @@ set(viewer_SOURCE_FILES llfloaterhandler.cpp llfloaterhardwaresettings.cpp llfloaterhelpbrowser.cpp + llfloaterhoverheight.cpp llfloaterhud.cpp llfloaterimagepreview.cpp llfloaterimsessiontab.cpp @@ -850,6 +851,7 @@ set(viewer_HEADER_FILES llfloaterhandler.h llfloaterhardwaresettings.h llfloaterhelpbrowser.h + llfloaterhoverheight.h llfloaterhud.h llfloaterimagepreview.h llfloaterimnearbychat.h diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index e5403775b2..0bbe2c1160 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -3.7.26 +3.7.27 diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index 15cb5bc0eb..de3732f339 100755 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -43,6 +43,7 @@ <key>tags</key> <array> <!-- sample entry for debugging specific items + <string>Avatar</string> <string>Inventory</string> <string>SceneLoadTiming</string> <string>Avatar</string> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 94d3c8a59f..138bdde9e9 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -654,6 +654,21 @@ <key>Value</key> <integer>2</integer> </map> + <key>AvatarPosFinalOffset</key> + <map> + <key>Comment</key> + <string>After-everything-else fixup for avatar position.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Vector3</string> + <key>Value</key> + <array> + <real>0.0</real> + <real>0.0</real> + <real>0.0</real> + </array> + </map> <key>AvatarPickerURL</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index fc6f1f6395..d119504017 100755 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -1,5 +1,16 @@ <llsd> <map> + <key>AvatarHoverOffsetZ</key> + <map> + <key>Comment</key> + <string>After-everything-else fixup for avatar Z position.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> <key>DoNotDisturbResponseChanged</key> <map> <key>Comment</key> diff --git a/indra/newview/llfloaterhoverheight.cpp b/indra/newview/llfloaterhoverheight.cpp new file mode 100755 index 0000000000..8908626de6 --- /dev/null +++ b/indra/newview/llfloaterhoverheight.cpp @@ -0,0 +1,157 @@ +/** +* @file llfloaterhoverheight.cpp +* @brief Controller for self avatar hover height +* @author vir@lindenlab.com +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, 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 "llfloaterhoverheight.h" +#include "llsliderctrl.h" +#include "llviewercontrol.h" +#include "llsdserialize.h" +#include "llhttpclient.h" +#include "llagent.h" +#include "llviewerregion.h" +#include "llvoavatarself.h" + +LLFloaterHoverHeight::LLFloaterHoverHeight(const LLSD& key) : LLFloater(key) +{ +} + +void LLFloaterHoverHeight::syncFromPreferenceSetting(void *user_data) +{ + F32 value = gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ"); + + LLFloaterHoverHeight *self = static_cast<LLFloaterHoverHeight*>(user_data); + LLSliderCtrl* sldrCtrl = self->getChild<LLSliderCtrl>("HoverHeightSlider"); + sldrCtrl->setValue(value,FALSE); + + if (isAgentAvatarValid()) + { + LLVector3 offset(0.0, 0.0, llclamp(value,MIN_HOVER_Z,MAX_HOVER_Z)); + LL_INFOS("Avatar") << "setting hover from preference setting " << offset[2] << LL_ENDL; + gAgentAvatarp->setHoverOffset(offset); + //gAgentAvatarp->sendHoverHeight(); + } +} + +BOOL LLFloaterHoverHeight::postBuild() +{ + LLSliderCtrl* sldrCtrl = getChild<LLSliderCtrl>("HoverHeightSlider"); + sldrCtrl->setMinValue(MIN_HOVER_Z); + sldrCtrl->setMaxValue(MAX_HOVER_Z); + sldrCtrl->setSliderMouseUpCallback(boost::bind(&LLFloaterHoverHeight::onFinalCommit,this)); + sldrCtrl->setSliderEditorCommitCallback(boost::bind(&LLFloaterHoverHeight::onFinalCommit,this)); + childSetCommitCallback("HoverHeightSlider", &LLFloaterHoverHeight::onSliderMoved, NULL); + + // Initialize slider from pref setting. + syncFromPreferenceSetting(this); + // Update slider on future pref changes. + if (gSavedPerAccountSettings.getControl("AvatarHoverOffsetZ")) + { + gSavedPerAccountSettings.getControl("AvatarHoverOffsetZ")->getCommitSignal()->connect(boost::bind(&syncFromPreferenceSetting, this)); + } + else + { + LL_WARNS() << "Control not found for AvatarHoverOffsetZ" << LL_ENDL; + } + + updateEditEnabled(); + + if (!mRegionChangedSlot.connected()) + { + mRegionChangedSlot = gAgent.addRegionChangedCallback(boost::bind(&LLFloaterHoverHeight::onRegionChanged,this)); + } + // Set up based on initial region. + onRegionChanged(); + + return TRUE; +} + +void LLFloaterHoverHeight::onClose(bool app_quitting) +{ + if (mRegionChangedSlot.connected()) + { + mRegionChangedSlot.disconnect(); + } +} + +// static +void LLFloaterHoverHeight::onSliderMoved(LLUICtrl* ctrl, void* userData) +{ + LLSliderCtrl* sldrCtrl = static_cast<LLSliderCtrl*>(ctrl); + F32 value = sldrCtrl->getValueF32(); + LLVector3 offset(0.0, 0.0, llclamp(value,MIN_HOVER_Z,MAX_HOVER_Z)); + LL_INFOS("Avatar") << "setting hover from slider moved" << offset[2] << LL_ENDL; + gAgentAvatarp->setHoverOffset(offset, false); +} + +// Do send-to-the-server work when slider drag completes, or new +// value entered as text. +void LLFloaterHoverHeight::onFinalCommit() +{ + LLSliderCtrl* sldrCtrl = getChild<LLSliderCtrl>("HoverHeightSlider"); + F32 value = sldrCtrl->getValueF32(); + gSavedPerAccountSettings.setF32("AvatarHoverOffsetZ",value); + + LLVector3 offset(0.0, 0.0, llclamp(value,MIN_HOVER_Z,MAX_HOVER_Z)); + LL_INFOS("Avatar") << "setting hover from slider final commit " << offset[2] << LL_ENDL; + gAgentAvatarp->setHoverOffset(offset, true); // will send update this time. +} + +void LLFloaterHoverHeight::onRegionChanged() +{ + LLViewerRegion *region = gAgent.getRegion(); + if (region && region->simulatorFeaturesReceived()) + { + updateEditEnabled(); + } + else if (region) + { + region->setSimulatorFeaturesReceivedCallback(boost::bind(&LLFloaterHoverHeight::onSimulatorFeaturesReceived,this,_1)); + } +} + +void LLFloaterHoverHeight::onSimulatorFeaturesReceived(const LLUUID ®ion_id) +{ + LLViewerRegion *region = gAgent.getRegion(); + if (region && (region->getRegionID()==region_id)) + { + updateEditEnabled(); + } +} + +void LLFloaterHoverHeight::updateEditEnabled() +{ + bool enabled = gAgent.getRegion() && gAgent.getRegion()->avatarHoverHeightEnabled(); + LLSliderCtrl* sldrCtrl = getChild<LLSliderCtrl>("HoverHeightSlider"); + sldrCtrl->setEnabled(enabled); + if (enabled) + { + syncFromPreferenceSetting(this); + } +} + + diff --git a/indra/newview/llfloaterhoverheight.h b/indra/newview/llfloaterhoverheight.h new file mode 100755 index 0000000000..ee065bc184 --- /dev/null +++ b/indra/newview/llfloaterhoverheight.h @@ -0,0 +1,52 @@ +/** +* @file llfloaterhoverheight.h +* @brief Controller for self avatar hover height. +* @author vir@lindenlab.com +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, 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_LLFLOATERHOVERHEIGHT_H +#define LL_LLFLOATERHOVERHEIGHT_H + +#include "llfloater.h" + +class LLFloaterHoverHeight: public LLFloater +{ +public: + LLFloaterHoverHeight(const LLSD& key); + BOOL postBuild(); + + static void onSliderMoved(LLUICtrl* ctrl, void* userData); + + void onFinalCommit(); + + static void syncFromPreferenceSetting(void *user_data); + + void onRegionChanged(); + void onSimulatorFeaturesReceived(const LLUUID ®ion_id); + void updateEditEnabled(); + + /*virtual*/ void onClose(bool app_quitting); + boost::signals2::connection mRegionChangedSlot; +}; + +#endif diff --git a/indra/newview/llfloaterperms.cpp b/indra/newview/llfloaterperms.cpp index 0880a5f35a..849aa7cd14 100755 --- a/indra/newview/llfloaterperms.cpp +++ b/indra/newview/llfloaterperms.cpp @@ -35,6 +35,8 @@ #include "llagent.h" #include "llviewerregion.h" #include "llnotificationsutil.h" +#include "llsdserialize.h" +#include "llvoavatar.h" LLFloaterPerms::LLFloaterPerms(const LLSD& seed) : LLFloater(seed) @@ -171,8 +173,9 @@ public: private: static std::string sPreviousReason; - void error(U32 status, const std::string& reason) + void httpFailure() { + const std::string& reason = getReason(); // Do not display the same error more than once in a row if (reason != sPreviousReason) { @@ -182,8 +185,12 @@ private: LLNotificationsUtil::add("DefaultObjectPermissions", args); } } - void result(const LLSD& content) + + void httpSuccess() { + //const LLSD& content = getContent(); + //dump_sequential_xml("perms_responder_result.xml", content); + // Since we have had a successful POST call be sure to display the next error message // even if it is the same as a previous one. sPreviousReason = ""; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 9da7717b74..d5f9268a64 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2210,6 +2210,8 @@ bool idle_startup() llassert(LLPathfindingManager::getInstance() != NULL); LLPathfindingManager::getInstance()->initSystem(); + gAgentAvatarp->sendHoverHeight(); + return TRUE; } diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index e19fe9ca75..fc18b20758 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -70,6 +70,7 @@ #include "llfloatergroups.h" #include "llfloaterhardwaresettings.h" #include "llfloaterhelpbrowser.h" +#include "llfloaterhoverheight.h" #include "llfloaterhud.h" #include "llfloaterimagepreview.h" #include "llfloaterimsession.h" @@ -221,7 +222,8 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("god_tools", "floater_god_tools.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGodTools>); LLFloaterReg::add("group_picker", "floater_choose_group.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGroupPicker>); - LLFloaterReg::add("help_browser", "floater_help_browser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHelpBrowser>); + LLFloaterReg::add("help_browser", "floater_help_browser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHelpBrowser>); + LLFloaterReg::add("edit_hover_height", "floater_edit_hover_height.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHoverHeight>); LLFloaterReg::add("hud", "floater_hud.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHUD>); LLFloaterReg::add("impanel", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterIMSession>); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index be9b4439a8..a23d02a00a 100755 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3881,6 +3881,14 @@ class LLEnableEditShape : public view_listener_t } }; +class LLEnableHoverHeight : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + return gAgent.getRegion() && gAgent.getRegion()->avatarHoverHeightEnabled(); + } +}; + class LLEnableEditPhysics : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -6075,6 +6083,11 @@ void handle_edit_shape() LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_shape")); } +void handle_hover_height() +{ + LLFloaterReg::showInstance("edit_hover_height"); +} + void handle_edit_physics() { LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_physics")); @@ -8567,10 +8580,12 @@ void initialize_menus() view_listener_t::addMenu(new LLEditEnableTakeOff(), "Edit.EnableTakeOff"); view_listener_t::addMenu(new LLEditEnableCustomizeAvatar(), "Edit.EnableCustomizeAvatar"); view_listener_t::addMenu(new LLEnableEditShape(), "Edit.EnableEditShape"); + view_listener_t::addMenu(new LLEnableHoverHeight(), "Edit.EnableHoverHeight"); view_listener_t::addMenu(new LLEnableEditPhysics(), "Edit.EnableEditPhysics"); commit.add("CustomizeAvatar", boost::bind(&handle_customize_avatar)); commit.add("EditOutfit", boost::bind(&handle_edit_outfit)); commit.add("EditShape", boost::bind(&handle_edit_shape)); + commit.add("HoverHeight", boost::bind(&handle_hover_height)); commit.add("EditPhysics", boost::bind(&handle_edit_physics)); // View menu diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 7d82ecf565..7ebe12cc07 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -425,6 +425,7 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, mCacheDirty(FALSE), mReleaseNotesRequested(FALSE), mCapabilitiesReceived(false), + mSimulatorFeaturesReceived(false), mBitsReceived(0.f), mPacketsReceived(0.f), mDead(FALSE), @@ -2039,6 +2040,26 @@ void LLViewerRegion::getInfo(LLSD& info) info["Region"]["Handle"]["y"] = (LLSD::Integer)y; } +boost::signals2::connection LLViewerRegion::setSimulatorFeaturesReceivedCallback(const caps_received_signal_t::slot_type& cb) +{ + return mSimulatorFeaturesReceivedSignal.connect(cb); +} + +void LLViewerRegion::setSimulatorFeaturesReceived(bool received) +{ + mSimulatorFeaturesReceived = received; + if (received) + { + mSimulatorFeaturesReceivedSignal(getRegionID()); + mSimulatorFeaturesReceivedSignal.disconnect_all_slots(); + } +} + +bool LLViewerRegion::simulatorFeaturesReceived() const +{ + return mSimulatorFeaturesReceived; +} + void LLViewerRegion::getSimulatorFeatures(LLSD& sim_features) const { sim_features = mSimulatorFeatures; @@ -2052,6 +2073,9 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features) LLSDSerialize::toPrettyXML(sim_features, str); LL_INFOS() << str.str() << LL_ENDL; mSimulatorFeatures = sim_features; + + setSimulatorFeaturesReceived(true); + } //this is called when the parent is not cacheable. @@ -3100,6 +3124,12 @@ bool LLViewerRegion::dynamicPathfindingEnabled() const return ( mSimulatorFeatures.has("DynamicPathfindingEnabled") && mSimulatorFeatures["DynamicPathfindingEnabled"].asBoolean()); } + +bool LLViewerRegion::avatarHoverHeightEnabled() const +{ + return ( mSimulatorFeatures.has("AvatarHoverHeightEnabled") && + mSimulatorFeatures["AvatarHoverHeightEnabled"].asBoolean()); +} /* Static Functions */ void log_capabilities(const CapabilityMap &capmap) diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index d01e746936..c14fa5aee8 100755 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -309,12 +309,19 @@ public: bool meshRezEnabled() const; bool meshUploadEnabled() const; + // has region received its simulator features list? Requires an additional query after caps received. + void setSimulatorFeaturesReceived(bool); + bool simulatorFeaturesReceived() const; + boost::signals2::connection setSimulatorFeaturesReceivedCallback(const caps_received_signal_t::slot_type& cb); + void getSimulatorFeatures(LLSD& info) const; void setSimulatorFeatures(const LLSD& info); bool dynamicPathfindingEnabled() const; + bool avatarHoverHeightEnabled() const; + typedef enum { CACHE_MISS_TYPE_FULL = 0, @@ -512,6 +519,7 @@ private: BOOL mCacheDirty; BOOL mAlive; // can become false if circuit disconnects BOOL mCapabilitiesReceived; + BOOL mSimulatorFeaturesReceived; BOOL mReleaseNotesRequested; BOOL mDead; //if true, this region is in the process of deleting. BOOL mPaused; //pause processing the objects in the region @@ -532,11 +540,13 @@ private: CacheMissItem::cache_miss_list_t mCacheMissList; caps_received_signal_t mCapabilitiesReceivedSignal; + caps_received_signal_t mSimulatorFeaturesReceivedSignal; + LLSD mSimulatorFeatures; // the materials capability throttle LLFrameTimer mMaterialsCapThrottleTimer; -LLFrameTimer mRenderInfoRequestTimer; + LLFrameTimer mRenderInfoRequestTimer; }; inline BOOL LLViewerRegion::getRegionProtocol(U64 protocol) const diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 444a26779a..a90fade3c9 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -111,6 +111,9 @@ extern F32 ANIM_SPEED_MAX; extern F32 ANIM_SPEED_MIN; extern U32 JOINT_COUNT_REQUIRED_FOR_FULLRIG; +const F32 MAX_HOVER_Z = 2.0; +const F32 MIN_HOVER_Z = -2.0; + // #define OUTPUT_BREAST_DATA using namespace LLAvatarAppearanceDefines; @@ -252,6 +255,8 @@ struct LLAppearanceMessageContents //U32 appearance_flags = 0; std::vector<F32> mParamWeights; std::vector<LLVisualParam*> mParams; + LLVector3 mHoverOffset; + bool mHoverOffsetWasSet; }; struct LLVOAvatarChildJoint : public LLInitParam::ChoiceBlock<LLVOAvatarChildJoint> @@ -719,7 +724,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mLastUpdateReceivedCOFVersion(-1) { //VTResume(); // VTune - + setHoverOffset(LLVector3(0.0, 0.0, 0.0)); + // mVoiceVisualizer is created by the hud effects manager and uses the HUD Effects pipeline const BOOL needsSendToSim = false; // currently, this HUD effect doesn't need to pack and unpack data to do its job mVoiceVisualizer = ( LLVoiceVisualizer *)LLHUDManager::getInstance()->createViewerEffect( LLHUDObject::LL_HUD_EFFECT_VOICE_VISUALIZER, needsSendToSim ); @@ -772,6 +778,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mRuthTimer.reset(); mRuthDebugTimer.reset(); mDebugExistenceTimer.reset(); + mLastAppearanceMessageTimer.reset(); if(LLSceneMonitor::getInstance()->isEnabled()) { @@ -1955,6 +1962,11 @@ U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys, // Do base class updates... U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp); + //LLTEContents tec; + //S32 te_retval = parseTEMessage(mesgsys, _PREHASH_ObjectData, block_num, tec); + + LL_DEBUGS("Avatar") << avString() << update_type << LL_ENDL; + // Print out arrival information once we have name of avatar. if (has_name && getNVPair("FirstName")) { @@ -3166,12 +3178,8 @@ void LLVOAvatar::forceUpdateVisualMuteSettings() } -//------------------------------------------------------------------------ -// updateCharacter() -// called on both your avatar and other avatars -//------------------------------------------------------------------------ -BOOL LLVOAvatar::updateCharacter(LLAgent &agent) -{ +void LLVOAvatar::updateDebugText() +{ // clear debug text mDebugText.clear(); @@ -3208,6 +3216,22 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) debug_line += llformat(" - cof rcv:%d", last_received_cof_version); } debug_line += llformat(" bsz-z: %f avofs-z: %f", mBodySize[2], mAvatarOffset[2]); + bool hover_enabled = getRegion() && getRegion()->avatarHoverHeightEnabled(); + debug_line += hover_enabled ? " H" : " h"; + const LLVector3& hover_offset = getHoverOffset(); + if (hover_offset[2] != 0.0) + { + debug_line += llformat(" hov_z: %f", hover_offset[2]); + debug_line += llformat(" %s", (mIsSitting ? "S" : "T")); + debug_line += llformat("%s", (isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED) ? "G" : "-")); + } + F32 elapsed = mLastAppearanceMessageTimer.getElapsedTimeF32(); + static const char *elapsed_chars = "Xx*..."; + U32 bucket = U32(elapsed*2); + if (bucket < strlen(elapsed_chars)) + { + debug_line += llformat(" %c", elapsed_chars[bucket]); + } addDebugText(debug_line); } if (gSavedSettings.getBOOL("DebugAvatarCompositeBaked")) @@ -3215,7 +3239,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) if (!mBakedTextureDebugText.empty()) addDebugText(mBakedTextureDebugText); } - + if (LLVOAvatar::sShowAnimationDebug) { for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin(); @@ -3244,6 +3268,27 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) } } + if (!mDebugText.size() && mText.notNull()) + { + mText->markDead(); + mText = NULL; + } + else if (mDebugText.size()) + { + setDebugText(mDebugText); + } + mDebugText.clear(); + +} + +//------------------------------------------------------------------------ +// updateCharacter() +// called on both your avatar and other avatars +//------------------------------------------------------------------------ +BOOL LLVOAvatar::updateCharacter(LLAgent &agent) +{ + updateDebugText(); + if (!mIsBuilt) { return FALSE; @@ -3352,9 +3397,15 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) LLVector3 xyVel = getVelocity(); xyVel.mV[VZ] = 0.0f; speed = xyVel.length(); - + // remembering the value here prevents a display glitch if the + // animation gets toggled during this update. + bool was_sit_ground_constrained = isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED); + if (!(mIsSitting && getParent())) { + // This case includes all configurations except sitting on an + // object, so does include ground sit. + //-------------------------------------------------------------------- // get timing info // handle initial condition case @@ -3408,9 +3459,14 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) // correct for the fact that the pelvis is not necessarily the center // of the agent's physical representation root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot; + if (!mIsSitting && !was_sit_ground_constrained) + { + root_pos += LLVector3d(getHoverOffset()); + } LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos); + if (newPosition != mRoot->getXform()->getWorldPosition()) { mRoot->touch(); @@ -3575,7 +3631,9 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) } else if (mDrawable.notNull()) { - mRoot->setPosition(mDrawable->getPosition()); + LLVector3 pos = mDrawable->getPosition(); + pos += getHoverOffset() * mDrawable->getRotation(); + mRoot->setPosition(pos); mRoot->setRotation(mDrawable->getRotation()); } @@ -3594,7 +3652,21 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) { updateMotions(LLCharacter::NORMAL_UPDATE); } - + + // Special handling for sitting on ground. + if (!getParent() && (mIsSitting || was_sit_ground_constrained)) + { + + F32 off_z = LLVector3d(getHoverOffset()).mdV[VZ]; + if (off_z != 0.0) + { + LLVector3 pos = mRoot->getWorldPosition(); + pos.mV[VZ] += off_z; + mRoot->touch(); + mRoot->setWorldPosition(pos); + } + } + // update head position updateHeadOffset(); @@ -3678,17 +3750,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) mRoot->updateWorldMatrixChildren(); - if (!mDebugText.size() && mText.notNull()) - { - mText->markDead(); - mText = NULL; - } - else if (mDebugText.size()) - { - setDebugText(mDebugText); - } - mDebugText.clear(); - //mesh vertices need to be reskinned mNeedsSkin = TRUE; return TRUE; @@ -7126,6 +7187,17 @@ void LLVOAvatar::parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMe // For future use: //mesgsys->getU32Fast(_PREHASH_AppearanceData, _PREHASH_Flags, appearance_flags, 0); } + + // Parse the AppearanceData field, if any. + contents.mHoverOffsetWasSet = false; + if (mesgsys->has(_PREHASH_AppearanceHover)) + { + LLVector3 hover; + mesgsys->getVector3Fast(_PREHASH_AppearanceHover, _PREHASH_HoverHeight, hover); + LL_DEBUGS("Avatar") << avString() << " hover received " << hover.mV[ VX ] << "," << hover.mV[ VY ] << "," << hover.mV[ VZ ] << LL_ENDL; + contents.mHoverOffset = hover; + contents.mHoverOffsetWasSet = true; + } // Parse visual params, if any. S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_VisualParam); @@ -7243,6 +7315,8 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) return; } + mLastAppearanceMessageTimer.reset(); + ESex old_sex = getSex(); LLAppearanceMessageContents contents; @@ -7427,6 +7501,22 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) } } + if (contents.mHoverOffsetWasSet && !isSelf()) + { + // Got an update for some other avatar + // Ignore updates for self, because we have a more authoritative value in the preferences. + setHoverOffset(contents.mHoverOffset); + LL_INFOS("Avatar") << avString() << "setting hover from message" << contents.mHoverOffset[2] << LL_ENDL; + } + + if (!contents.mHoverOffsetWasSet && !isSelf()) + { + // If we don't get a value at all, we are presumably in a + // region that does not support hover height. + LL_WARNS() << avString() << "zeroing hover because not defined in appearance message" << LL_ENDL; + setHoverOffset(LLVector3(0.0, 0.0, 0.0)); + } + setCompositeUpdatesEnabled( TRUE ); // If all of the avatars are completely baked, release the global image caches to conserve memory. diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 2223acc893..2e72633d39 100755 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -214,7 +214,6 @@ public: /*virtual*/ LLVector3 getPosAgentFromGlobal(const LLVector3d &position); virtual void updateVisualParams(); - /** Inherited ** ** *******************************************************************************/ @@ -234,6 +233,7 @@ private: //aligned members // Updates //-------------------------------------------------------------------- public: + void updateDebugText(); virtual BOOL updateCharacter(LLAgent &agent); void idleUpdateVoiceVisualizer(bool voice_enabled); void idleUpdateMisc(bool detailed_update); @@ -988,6 +988,7 @@ public: protected: LLFrameTimer mRuthDebugTimer; // For tracking how long it takes for av to rez LLFrameTimer mDebugExistenceTimer; // Debugging for how long the avatar has been in memory. + LLFrameTimer mLastAppearanceMessageTimer; // Time since last appearance message received. //-------------------------------------------------------------------- // COF monitoring @@ -1020,6 +1021,9 @@ protected: // Shared with LLVOAvatarSelf extern const F32 SELF_ADDITIONAL_PRI; extern const S32 MAX_TEXTURE_VIRTUAL_SIZE_RESET_INTERVAL; +extern const F32 MAX_HOVER_Z; +extern const F32 MIN_HOVER_Z; + std::string get_sequential_numbered_file_name(const std::string& prefix, const std::string& suffix); void dump_sequential_xml(const std::string outprefix, const LLSD& content); diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 3f3cd25f95..ae7a233876 100755 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -127,6 +127,25 @@ struct LocalTextureData LLTextureEntry *mTexEntry; }; +// TODO - this class doesn't really do anything, could just use a base +// class responder if nothing else gets added. +class LLHoverHeightResponder: public LLHTTPClient::Responder +{ +public: + LLHoverHeightResponder(): LLHTTPClient::Responder() {} + +private: + void httpFailure() + { + LL_WARNS() << dumpResponse() << LL_ENDL; + } + + void httpSuccess() + { + LL_INFOS() << dumpResponse() << LL_ENDL; + } +}; + //----------------------------------------------------------------------------- // Callback data //----------------------------------------------------------------------------- @@ -159,7 +178,10 @@ LLVOAvatarSelf::LLVOAvatarSelf(const LLUUID& id, mScreenp(NULL), mLastRegionHandle(0), mRegionCrossingCount(0), - mInitialBakesLoaded(false) + mInitialBakesLoaded(false), + // Value outside legal range, so will always be a mismatch the + // first time through. + mLastHoverOffsetSent(LLVector3(0.0f, 0.0f, -999.0f)) { mMotionController.mIsSelf = TRUE; @@ -220,11 +242,40 @@ void LLVOAvatarSelf::initInstance() return; } + setHoverIfRegionEnabled(); + //doPeriodically(output_self_av_texture_diagnostics, 30.0); doPeriodically(update_avatar_rez_metrics, 5.0); doPeriodically(boost::bind(&LLVOAvatarSelf::checkStuckAppearance, this), 30.0); } +void LLVOAvatarSelf::setHoverIfRegionEnabled() +{ + if (getRegion() && getRegion()->simulatorFeaturesReceived()) + { + if (getRegion()->avatarHoverHeightEnabled()) + { + F32 hover_z = gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ"); + setHoverOffset(LLVector3(0.0, 0.0, llclamp(hover_z,MIN_HOVER_Z,MAX_HOVER_Z))); + LL_INFOS("Avatar") << avString() << " set hover height from debug setting " << hover_z << LL_ENDL; + } + else + { + setHoverOffset(LLVector3(0.0, 0.0, 0.0)); + LL_INFOS("Avatar") << avString() << " zeroing hover height, region does not support" << LL_ENDL; + } + } + else + { + LL_INFOS("Avatar") << avString() << " region or simulator features not known, no change on hover" << LL_ENDL; + if (getRegion()) + { + getRegion()->setSimulatorFeaturesReceivedCallback(boost::bind(&LLVOAvatarSelf::onSimulatorFeaturesReceived,this,_1)); + } + + } +} + bool LLVOAvatarSelf::checkStuckAppearance() { const F32 CONDITIONAL_UNSTICK_INTERVAL = 300.0; @@ -835,6 +886,12 @@ void LLVOAvatarSelf::removeMissingBakedTextures() } } +void LLVOAvatarSelf::onSimulatorFeaturesReceived(const LLUUID& region_id) +{ + LL_INFOS("Avatar") << "simulator features received, setting hover based on region props" << LL_ENDL; + setHoverIfRegionEnabled(); +} + //virtual void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp) { @@ -853,6 +910,17 @@ void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp) //LL_INFOS() << "pos_from_old_region is " << global_pos_from_old_region // << " while pos_from_new_region is " << pos_from_new_region // << LL_ENDL; + + // Update hover height, or schedule callback, based on whether + // it's supported in this region. + if (regionp->simulatorFeaturesReceived()) + { + setHoverIfRegionEnabled(); + } + else + { + regionp->setSimulatorFeaturesReceivedCallback(boost::bind(&LLVOAvatarSelf::onSimulatorFeaturesReceived,this,_1)); + } } if (!regionp || (regionp->getHandle() != mLastRegionHandle)) @@ -2709,6 +2777,39 @@ bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const return success; } +//------------------------------------------------------------------------ +// sendHoverHeight() +//------------------------------------------------------------------------ +void LLVOAvatarSelf::sendHoverHeight() const +{ + std::string url = gAgent.getRegion()->getCapability("AgentPreferences"); + + if (!url.empty()) + { + LLSD update = LLSD::emptyMap(); + const LLVector3& hover_offset = getHoverOffset(); + update["hover_height"] = hover_offset[2]; + + LL_DEBUGS("Avatar") << avString() << "sending hover height value " << hover_offset[2] << LL_ENDL; + LLHTTPClient::post(url, update, new LLHoverHeightResponder); + + mLastHoverOffsetSent = hover_offset; + } +} + +void LLVOAvatarSelf::setHoverOffset(const LLVector3& hover_offset, bool send_update) +{ + if (getHoverOffset() != hover_offset) + { + LL_INFOS("Avatar") << avString() << " setting hover due to change " << hover_offset[2] << LL_ENDL; + LLVOAvatar::setHoverOffset(hover_offset, send_update); + } + if (send_update && (hover_offset != mLastHoverOffsetSent)) + { + LL_INFOS("Avatar") << avString() << " sending hover due to change " << hover_offset[2] << LL_ENDL; + sendHoverHeight(); + } +} //------------------------------------------------------------------------ // needsRenderBeam() diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 5f36872575..b8e9bbb77a 100755 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -75,6 +75,9 @@ protected: // LLViewerObject interface and related //-------------------------------------------------------------------- public: + boost::signals2::connection mRegionChangedSlot; + + void onSimulatorFeaturesReceived(const LLUUID& region_id); /*virtual*/ void updateRegion(LLViewerRegion *regionp); /*virtual*/ void idleUpdate(LLAgent &agent, const F64 &time); @@ -328,6 +331,14 @@ public: public: bool sendAppearanceMessage(LLMessageSystem *mesgsys) const; + // -- care and feeding of hover height. + void setHoverIfRegionEnabled(); + void sendHoverHeight() const; + /*virtual*/ void setHoverOffset(const LLVector3& hover_offset, bool send_update=true); + +private: + mutable LLVector3 mLastHoverOffsetSent; + /** Appearance ** ** *******************************************************************************/ diff --git a/indra/newview/skins/default/xui/en/floater_edit_hover_height.xml b/indra/newview/skins/default/xui/en/floater_edit_hover_height.xml new file mode 100755 index 0000000000..8ec6735a01 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_edit_hover_height.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + positioning="cascading" + ignore_ui_scale="false" + legacy_header_height="225" + can_minimize="true" + can_close="true" + can_resize="true" + min_height="65" + min_width="515" + height="65" + layout="topleft" + name="HoverHeight" + single_instance="true" + help_topic="hover_height" + save_rect="true" + save_visibility="true" + title="SET HOVER HEIGHT" + width="515"> + <slider + enabled="false" + control_name="HoverHeightSlider" + decimal_digits="3" + follows="top|left" + height="15" + increment="0.001" + initial_value="0.0" + label="Height" + label_width="60" + left="10" + width="501" + layout="topleft" + name="HoverHeightSlider" + top="35" + can_edit_text="true" + > + </slider> +</floater> diff --git a/indra/newview/skins/default/xui/en/menu_attachment_self.xml b/indra/newview/skins/default/xui/en/menu_attachment_self.xml index bcbc8d5b86..c6ae844d67 100755 --- a/indra/newview/skins/default/xui/en/menu_attachment_self.xml +++ b/indra/newview/skins/default/xui/en/menu_attachment_self.xml @@ -91,6 +91,14 @@ name="Edit Outfit"> <menu_item_call.on_enable function="Edit.EnableEditShape" /> </menu_item_call> + <menu_item_call label="Hover Height" + layout="topleft" + name="Hover Height"> + <menu_item_call.on_click + function="HoverHeight" /> + <menu_item_call.on_enable + function="Edit.EnableHoverHeight" /> + </menu_item_call> <menu_item_call label="My Friends" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/menu_avatar_self.xml b/indra/newview/skins/default/xui/en/menu_avatar_self.xml index ca0c9bd5e4..d3b0b07f70 100755 --- a/indra/newview/skins/default/xui/en/menu_avatar_self.xml +++ b/indra/newview/skins/default/xui/en/menu_avatar_self.xml @@ -229,6 +229,14 @@ <menu_item_call.on_enable function="Edit.EnableEditShape" /> </menu_item_call> + <menu_item_call label="Hover Height" + layout="topleft" + name="Hover Height"> + <menu_item_call.on_click + function="HoverHeight" /> + <menu_item_call.on_enable + function="Edit.EnableHoverHeight" /> + </menu_item_call> <menu_item_call label="My Friends" layout="topleft" diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg index 6702de9b4a..3cec4ada1c 100755 --- a/scripts/messages/message_template.msg +++ b/scripts/messages/message_template.msg @@ -3594,6 +3594,10 @@ version 2.0 { CofVersion S32 } { Flags U32 } } + { + AppearanceHover Variable + { HoverHeight LLVector3 } + } } // AvatarSitResponse - response to a request to sit on an object diff --git a/scripts/messages/message_template.msg.sha1 b/scripts/messages/message_template.msg.sha1 index 7a31177f11..e699efb03c 100755 --- a/scripts/messages/message_template.msg.sha1 +++ b/scripts/messages/message_template.msg.sha1 @@ -1 +1 @@ -4dbf88396c3188ad4c54c4f847a7d8817793668d
\ No newline at end of file +2286adc795b1b06eb86fdda431a71a6f0874b4f1
\ No newline at end of file |