diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h
index 085b681854f0ee506cdcc89c96cbb4977f77261f..1a3e3076634416ef83f902169949461d05542d38 100755
--- a/indra/llcharacter/llcharacter.h
+++ b/indra/llcharacter/llcharacter.h
@@ -261,7 +261,8 @@ class LLCharacter
 	static std::vector< LLCharacter* > sInstances;
 	static BOOL sAllowInstancesChange ; //debug use
 
-	LLVector3 mHoverOffset;
+	virtual void	setHoverOffset(const LLVector3& hover_offset, bool send_update=true) { mHoverOffset = hover_offset; }
+	const LLVector3& getHoverOffset() const { return mHoverOffset; }
 
 protected:
 	LLMotionController	mMotionController;
@@ -275,7 +276,6 @@ class LLCharacter
 	U32					mSkeletonSerialNum;
 	LLAnimPauseRequest	mPauseRequest;
 
-
 private:
 	// visual parameter stuff
 	typedef std::map<S32, LLVisualParam *> 		visual_param_index_map_t;
@@ -286,6 +286,8 @@ class LLCharacter
 	visual_param_name_map_t  					mVisualParamNameMap;
 
 	static LLStringTable sVisualParamNames;	
+
+	LLVector3 mHoverOffset;
 };
 
 #endif // LL_LLCHARACTER_H
diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp
index 89bec40323ca2fc03973e3eb11cb40e4b3a5cfca..5317e230c821ea2b0a3a3a3007a334b8d559ff70 100755
--- a/indra/llcharacter/llkeyframemotion.cpp
+++ b/indra/llcharacter/llkeyframemotion.cpp
@@ -984,7 +984,7 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8
 	{
 	case CONSTRAINT_TARGET_TYPE_GROUND:
 		target_pos = mCharacter->getPosAgentFromGlobal(constraint->mGroundPos);
-		target_pos += mCharacter->mHoverOffset;
+		target_pos += mCharacter->getHoverOffset();
 //		LL_INFOS() << "Target Pos " << constraint->mGroundPos << " on " << mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume)->getName() << LL_ENDL;
 		break;
 	case CONSTRAINT_TARGET_TYPE_BODY:
diff --git a/indra/newview/llfloaterhoverheight.cpp b/indra/newview/llfloaterhoverheight.cpp
index 7d0c011b74628eb8bd193ee4306ca1b43a74983e..8908626de6a737aa7cd46ef32ed00f51b76a42ac 100755
--- a/indra/newview/llfloaterhoverheight.cpp
+++ b/indra/newview/llfloaterhoverheight.cpp
@@ -51,9 +51,9 @@ void LLFloaterHoverHeight::syncFromPreferenceSetting(void *user_data)
 	if (isAgentAvatarValid())
 	{
 		LLVector3 offset(0.0, 0.0, llclamp(value,MIN_HOVER_Z,MAX_HOVER_Z));
-		gAgentAvatarp->mHoverOffset = offset;
-		LL_INFOS("Avatar") << "set hover from preference setting" << offset[2] << LL_ENDL;
-		gAgentAvatarp->sendHoverHeight();
+		LL_INFOS("Avatar") << "setting hover from preference setting " << offset[2] << LL_ENDL;
+		gAgentAvatarp->setHoverOffset(offset);
+		//gAgentAvatarp->sendHoverHeight();
 	}
 }
 
@@ -84,18 +84,28 @@ BOOL LLFloaterHoverHeight::postBuild()
 	{
 		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") << "set hover from slider moved" << offset[2] << LL_ENDL;
-	gAgentAvatarp->mHoverOffset = offset;
+	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
@@ -105,6 +115,10 @@ 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()
diff --git a/indra/newview/llfloaterhoverheight.h b/indra/newview/llfloaterhoverheight.h
index 8809fc1bf85488f7eebdc041e6a4b9e6ba6f7ec3..ee065bc184a18c7d9749bea8959b0909a07d2e17 100755
--- a/indra/newview/llfloaterhoverheight.h
+++ b/indra/newview/llfloaterhoverheight.h
@@ -45,8 +45,8 @@ class LLFloaterHoverHeight: public LLFloater
 	void onSimulatorFeaturesReceived(const LLUUID &region_id);
 	void updateEditEnabled();
 
-
-	boost::signals2::connection                   mRegionChangedSlot;
+	/*virtual*/ void onClose(bool app_quitting);
+	boost::signals2::connection mRegionChangedSlot;
 };
 
 #endif
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index b5bf174045dd512fe4fbc94cab1754f4a85c7e0b..38420a31bc6677222e2e44832f97562dccd8fac2 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -724,7 +724,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mLastUpdateReceivedCOFVersion(-1)
 {
 	//VTResume();  // VTune
-	mHoverOffset = LLVector3(0.0, 0.0, 0.0);
+	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 );
@@ -3221,9 +3222,10 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 		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";
-		if (mHoverOffset[2] != 0.0)
+		const LLVector3& hover_offset = getHoverOffset();
+		if (hover_offset[2] != 0.0)
 		{
-			debug_line += llformat(" hov_z: %f", mHoverOffset[2]);
+			debug_line += llformat(" hov_z: %f", hover_offset[2]);
 		}
 		F32 elapsed = mLastAppearanceMessageTimer.getElapsedTimeF32();
 		static const char *elapsed_chars = "Xx*...";
@@ -3432,7 +3434,7 @@ 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;
-		root_pos += LLVector3d(mHoverOffset);
+		root_pos += LLVector3d(getHoverOffset());
 		
 		LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos);
 
@@ -3602,7 +3604,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 	else if (mDrawable.notNull())
 	{
 		LLVector3 pos = mDrawable->getPosition();
-		pos += mHoverOffset * mDrawable->getRotation();
+		pos += getHoverOffset() * mDrawable->getRotation();
 		mRoot->setPosition(pos);
 		mRoot->setRotation(mDrawable->getRotation());
 	}
@@ -7463,8 +7465,8 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 	{
 		// Got an update for some other avatar
 		// Ignore updates for self, because we have a more authoritative value in the preferences.
-		mHoverOffset = contents.mHoverOffset;
-		LL_INFOS("Avatar") << avString() << "setting hover from message" << mHoverOffset[2] << LL_ENDL;
+		setHoverOffset(contents.mHoverOffset);
+		LL_INFOS("Avatar") << avString() << "setting hover from message" << contents.mHoverOffset[2] << LL_ENDL;
 	}
 
 	if (!contents.mHoverOffsetWasSet && !isSelf())
@@ -7472,7 +7474,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 		// 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;
-		mHoverOffset = LLVector3(0.0, 0.0, 0.0);
+		setHoverOffset(LLVector3(0.0, 0.0, 0.0));
 	}
 
 	setCompositeUpdatesEnabled( TRUE );
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 5341c6f1c85d8bebfa2cf905788f79e0391311f7..bd7ab41258c06304ba9775dcec7cf3ff6c71eb2b 100755
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -178,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;
 
@@ -253,12 +256,12 @@ void LLVOAvatarSelf::setHoverIfRegionEnabled()
 		if (getRegion()->avatarHoverHeightEnabled())
 		{
 			F32 hover_z = gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ");
-			mHoverOffset = LLVector3(0.0, 0.0, llclamp(hover_z,MIN_HOVER_Z,MAX_HOVER_Z));
-			LL_INFOS("Avatar") << avString() << " set hover height from debug setting " << mHoverOffset[2] << LL_ENDL;
+			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 
 		{
-			mHoverOffset = LLVector3(0.0, 0.0, 0.0);
+			setHoverOffset(LLVector3(0.0, 0.0, 0.0));
 			LL_INFOS("Avatar") << avString() << " zeroing hover height, region does not support" << LL_ENDL;
 		}
 	}
@@ -2776,10 +2779,27 @@ void LLVOAvatarSelf::sendHoverHeight() const
 	if (!url.empty())
 	{
 		LLSD update = LLSD::emptyMap();
-		update["hover_height"] = mHoverOffset[2];
+		const LLVector3& hover_offset = getHoverOffset();
+		update["hover_height"] = hover_offset[2];
 
-		LL_DEBUGS("Avatar") << avString() << "sending hover height value " << mHoverOffset[2] << LL_ENDL;
+		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();
 	}
 }
 
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index 6e585520dab865ea8f2ef3647e1f7be94f200172..6d190b5cfe7e911952837f6ec42aafe6b8282c30 100755
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -333,6 +333,10 @@ class LLVOAvatarSelf :
 	// -- 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
  **                                                                            **