Subject: SL-10238: Viewer spport for push notifications from the simulator contaiting partial groups of settings. Blend these settings into the current environment.

From: Rider Linden <[email-masked]>
Date: Wed, 12 Dec 2018 14:07:23 -0800

---
 indra/llinventory/llsettingsbase.cpp       |  44 ++--
 indra/llinventory/llsettingsbase.h         |  19 +-
 indra/llinventory/llsettingsdaycycle.cpp   |   1 -
 indra/llinventory/llsettingssky.cpp        |  12 ++
 indra/llinventory/llsettingssky.h          |   1 +
 indra/llinventory/llsettingswater.cpp      |   8 +
 indra/llinventory/llsettingswater.h        |   1 +
 indra/llmessage/lldispatcher.cpp           | 106 +++++----
 indra/llmessage/lldispatcher.h             |   6 +
 indra/llmessage/message_prehash.cpp        |   1 +
 indra/llmessage/message_prehash.h          |   2 +
 indra/newview/llenvironment.cpp            | 336 +++++++++++++++++++++++++----
 indra/newview/llenvironment.h              |  89 ++++++--
 indra/newview/lleventpoll.cpp              |   5 +-
 indra/newview/llstartup.cpp                |   3 +-
 indra/newview/llviewergenericmessage.cpp   |  24 ++-
 indra/newview/llviewergenericmessage.h     |   1 +
 scripts/messages/message_template.msg      |  24 ++-
 scripts/messages/message_template.msg.sha1 |   2 +-
 19 files changed, 556 insertions(+), 129 deletions(-)

(limited to 'scripts/messages/message_template.msg')

diff --git a/indra/llinventory/llsettingsbase.cpp b/indra/llinventory/llsettingsbase.cpp
index 7917fa96f1..5adb787048 100644
--- a/indra/llinventory/llsettingsbase.cpp
+++ b/indra/llinventory/llsettingsbase.cpp
@@ -60,6 +60,8 @@ const U32 LLSettingsBase::FLAG_NOCOPY(0x01 << 0);
 const U32 LLSettingsBase::FLAG_NOMOD(0x01 << 1);
 const U32 LLSettingsBase::FLAG_NOTRANS(0x01 << 2);
 
+const U32 LLSettingsBase::Validator::VALIDATION_PARTIAL(0x01 << 0);
+
 //=========================================================================
 LLSettingsBase::LLSettingsBase():
     mSettings(LLSD::emptyMap()),
@@ -385,7 +387,7 @@ bool LLSettingsBase::validate()
     return result["success"].asBoolean();
 }
 
-LLSD LLSettingsBase::settingValidation(LLSD &settings, validation_list_t &validations)
+LLSD LLSettingsBase::settingValidation(LLSD &settings, validation_list_t &validations, bool partial)
 {
     static Validator  validateName(SETTING_NAME, false, LLSD::TypeString, boost::bind(&Validator::verifyStringLength, _1, 32));
     static Validator  validateId(SETTING_ID, false, LLSD::TypeUUID);
@@ -398,44 +400,48 @@ LLSD LLSettingsBase::settingValidation(LLSD &settings, validation_list_t &valida
     bool              isValid(true);
     LLSD              errors(LLSD::emptyArray());
     LLSD              warnings(LLSD::emptyArray());
+    U32               flags(0);
+    
+    if (partial)
+        flags |= Validator::VALIDATION_PARTIAL;
 
     // Fields common to all settings.
-    if (!validateName.verify(settings))
+    if (!validateName.verify(settings, flags))
     {
         errors.append( LLSD::String("Unable to validate 'name'.") );
         isValid = false;
     }
     validated.insert(validateName.getName());
 
-    if (!validateId.verify(settings))
+    if (!validateId.verify(settings, flags))
     {
         errors.append( LLSD::String("Unable to validate 'id'.") );
         isValid = false;
     }
     validated.insert(validateId.getName());
 
-    if (!validateHash.verify(settings))
+    if (!validateHash.verify(settings, flags))
     {
         errors.append( LLSD::String("Unable to validate 'hash'.") );
         isValid = false;
     }
     validated.insert(validateHash.getName());
 
-    if (!validateAssetId.verify(settings))
+    if (!validateAssetId.verify(settings, flags))
     {
         errors.append(LLSD::String("Invalid asset Id"));
         isValid = false;
     }
     validated.insert(validateAssetId.getName());
 
-    if (!validateType.verify(settings))
+    if (!validateType.verify(settings, flags))
     {
         errors.append( LLSD::String("Unable to validate 'type'.") );
         isValid = false;
     }
     validated.insert(validateType.getName());
 
-    if (!validateFlags.verify(settings))
+    if (!validateFlags.verify(settings, flags))
     {
         errors.append(LLSD::String("Unable to validate 'flags'."));
         isValid = false;
@@ -453,7 +459,7 @@ LLSD LLSettingsBase::settingValidation(LLSD &settings, validation_list_t &valida
         }
 #endif
 
-        if (!(*itv).verify(settings))
+        if (!(*itv).verify(settings, flags))
         {
             std::stringstream errtext;
 
@@ -498,10 +504,14 @@ LLSD LLSettingsBase::settingValidation(LLSD &settings, validation_list_t &valida
 }
 
 //=========================================================================
-bool LLSettingsBase::Validator::verify(LLSD &data)
+
+bool LLSettingsBase::Validator::verify(LLSD &data, U32 flags)
 {
     if (!data.has(mName) || (data.has(mName) && data[mName].isUndefined()))
     {
+        if ((flags & VALIDATION_PARTIAL) != 0) // we are doing a partial validation.  Do no attempt to set a default if missing (or fail even if required)
+            return true;    
+        
         if (!mDefault.isUndefined())
         {
             data[mName] = mDefault;
@@ -667,7 +677,10 @@ bool LLSettingsBase::Validator::verifyStringLength(LLSD &value, S32 length)
 //=========================================================================
 void LLSettingsBlender::update(const LLSettingsBase::BlendFactor& blendf)
 {
-    setBlendFactor(blendf);
+    F64 res = setBlendFactor(blendf);
+
+    if ((res >= 0.0001) && (res < 1.0))
+        mTarget->update();
 }
 
 F64 LLSettingsBlender::setBlendFactor(const LLSettingsBase::BlendFactor& blendf_in)
@@ -688,7 +701,6 @@ F64 LLSettingsBlender::setBlendFactor(const LLSettingsBase::BlendFactor& blendf_
             return blendf;
         }
         mTarget->blend(mFinal, blendf);
-        mTarget->update();
     }
     else
     {
@@ -715,7 +727,7 @@ LLSettingsBase::BlendFactor LLSettingsBlenderTimeDelta::calculateBlend(const LLS
     return LLSettingsBase::BlendFactor(fmod((F64)spanpos, (F64)spanlen) / (F64)spanlen);
 }
 
-void LLSettingsBlenderTimeDelta::applyTimeDelta(const LLSettingsBase::Seconds& timedelta)
+bool LLSettingsBlenderTimeDelta::applyTimeDelta(const LLSettingsBase::Seconds& timedelta)
 {
     mTimeSpent += timedelta;
     mTimeDeltaPassed += timedelta;
@@ -724,12 +736,12 @@ void LLSettingsBlenderTimeDelta::applyTimeDelta(const LLSettingsBase::Seconds& t
     {
         mIgnoreTimeDelta = false;
         triggerComplete();
-        return;
+        return false;
     }
 
     if ((mTimeDeltaPassed < mTimeDeltaThreshold) && (!mIgnoreTimeDelta))
     {
-        return;
+        return false;
     }
 
     LLSettingsBase::BlendFactor blendf = calculateBlend(mTimeSpent, mBlendSpan);
@@ -737,10 +749,10 @@ void LLSettingsBlenderTimeDelta::applyTimeDelta(const LLSettingsBase::Seconds& t
 
     if (fabs(mLastBlendF - blendf) < mBlendFMinDelta)
     {
-        return;
+        return false;
     }
 
     mLastBlendF = blendf;
-
     update(blendf);
+    return true;
 }
diff --git a/indra/llinventory/llsettingsbase.h b/indra/llinventory/llsettingsbase.h
index c7b685c6d5..87466e6570 100644
--- a/indra/llinventory/llsettingsbase.h
+++ b/indra/llinventory/llsettingsbase.h
@@ -266,6 +266,8 @@ public:
     class Validator
     {
     public:
+        static const U32 VALIDATION_PARTIAL;
+        
         typedef boost::function<bool(LLSD &)> verify_pr;
 
         Validator(std::string name, bool required, LLSD::Type type, verify_pr verify = verify_pr(), LLSD defval = LLSD())  :
@@ -280,7 +282,7 @@ public:
         bool        isRequired() const { return mRequired; }
         LLSD::Type  getType() const { return mType; }
 
-        bool        verify(LLSD &data);
+        bool        verify(LLSD &data, U32 flags);
 
         // Some basic verifications
         static bool verifyColor(LLSD &value);
@@ -302,7 +304,7 @@ public:
     };
     typedef std::vector<Validator> validation_list_t;
 
-    static LLSD settingValidation(LLSD &settings, validation_list_t &validations);
+    static LLSD settingValidation(LLSD &settings, validation_list_t &validations, bool partial = false);
 
     inline void setAssetId(LLUUID value)
     {   // note that this skips setLLSD
@@ -346,7 +348,7 @@ protected:
     virtual stringset_t getSlerpKeys() const { return stringset_t(); }
 
     // Calculate any custom settings that may need to be cached.
-    virtual void updateSettings() { mDirty = false; mReplaced = false; };
+    virtual void updateSettings() { mDirty = false; mReplaced = false; }
 
     virtual validation_list_t getValidationList() const = 0;
 
@@ -366,6 +368,12 @@ protected:
         mBlendedFactor = blendfactor;
     }
 
+    void replaceWith(LLSettingsBase::ptr_t other)
+    {
+        replaceSettings(other->cloneSettings());
+        setBlendFactor(other->getBlendFactor());
+    }
+
 private:
     bool        mDirty;
     bool        mReplaced; // super dirty!
@@ -437,10 +445,11 @@ public:
     }
 
     virtual void            update(const LLSettingsBase::BlendFactor& blendf);
-    virtual void            applyTimeDelta(const LLSettingsBase::Seconds& delta)
+    virtual bool            applyTimeDelta(const LLSettingsBase::Seconds& timedelta)
     {
         llassert(false);
         // your derived class needs to implement an override of this func
+        return false;
     }
 
     virtual F64             setBlendFactor(const LLSettingsBase::BlendFactor& position);
@@ -495,7 +504,7 @@ public:
         mLastBlendF = LLSettingsBase::BlendFactor(-1.0f);
     }
 
-    virtual void applyTimeDelta(const LLSettingsBase::Seconds& timedelta) SETTINGS_OVERRIDE;
+    virtual bool applyTimeDelta(const LLSettingsBase::Seconds& timedelta) SETTINGS_OVERRIDE;
 
     inline void setTimeDeltaThreshold(const LLSettingsBase::Seconds time)
     {
diff --git a/indra/llinventory/llsettingsdaycycle.cpp b/indra/llinventory/llsettingsdaycycle.cpp
index ec497b4021..188e205176 100644
--- a/indra/llinventory/llsettingsdaycycle.cpp
+++ b/indra/llinventory/llsettingsdaycycle.cpp
@@ -203,7 +203,6 @@ bool LLSettingsDay::initialize(bool validate_frames)
     if (mSettings.has(SETTING_ASSETID))
     {
         assetid = mSettings[SETTING_ASSETID].asUUID();
-        LL_WARNS("LAPRAS") << "initializing daycycle with asset id " << assetid << LL_ENDL;
     }
 
     std::map<std::string, LLSettingsBase::ptr_t> used;
diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp
index ace530ae54..37882b91ec 100644
--- a/indra/llinventory/llsettingssky.cpp
+++ b/indra/llinventory/llsettingssky.cpp
@@ -431,6 +431,18 @@ void LLSettingsSky::replaceSettings(LLSD settings)
     mNextHaloTextureId.setNull();
 }
 
+void LLSettingsSky::replaceWithSky(LLSettingsSky::ptr_t pother)
+{
+    replaceWith(pother);
+
+    mNextSunTextureId = pother->mNextSunTextureId;
+    mNextMoonTextureId = pother->mNextMoonTextureId;
+    mNextCloudTextureId = pother->mNextCloudTextureId;
+    mNextBloomTextureId = pother->mNextBloomTextureId;
+    mNextRainbowTextureId = pother->mNextRainbowTextureId;
+    mNextHaloTextureId = pother->mNextHaloTextureId;
+}
+
 void LLSettingsSky::blend(const LLSettingsBase::ptr_t &end, F64 blendf) 
 {
     llassert(getSettingsType() == end->getSettingsType());
diff --git a/indra/llinventory/llsettingssky.h b/indra/llinventory/llsettingssky.h
index 796120ba03..dc652dc182 100644
--- a/indra/llinventory/llsettingssky.h
+++ b/indra/llinventory/llsettingssky.h
@@ -118,6 +118,7 @@ public:
     
     virtual void replaceSettings(LLSD settings) SETTINGS_OVERRIDE;
 
+    void replaceWithSky(LLSettingsSky::ptr_t pother);
     static LLSD defaults(const LLSettingsBase::TrackPosition& position = 0.0f);
 
     F32 getPlanetRadius() const;
diff --git a/indra/llinventory/llsettingswater.cpp b/indra/llinventory/llsettingswater.cpp
index 7cfff954a0..1780948f0a 100644
--- a/indra/llinventory/llsettingswater.cpp
+++ b/indra/llinventory/llsettingswater.cpp
@@ -197,6 +197,14 @@ void LLSettingsWater::replaceSettings(LLSD settings)
     mNextTransparentTextureID.setNull();
 }
 
+void LLSettingsWater::replaceWithWater(LLSettingsWater::ptr_t other)
+{
+    replaceWith(other);
+
+    mNextNormalMapID = other->mNextNormalMapID;
+    mNextTransparentTextureID = other->mNextTransparentTextureID;
+}
+
 LLSettingsWater::validation_list_t LLSettingsWater::getValidationList() const
 {
     return LLSettingsWater::validationList();
diff --git a/indra/llinventory/llsettingswater.h b/indra/llinventory/llsettingswater.h
index 009a72eb24..118c515743 100644
--- a/indra/llinventory/llsettingswater.h
+++ b/indra/llinventory/llsettingswater.h
@@ -65,6 +65,7 @@ public:
     virtual void blend(const LLSettingsBase::ptr_t &end, F64 blendf) SETTINGS_OVERRIDE;
 
     virtual void replaceSettings(LLSD settings) SETTINGS_OVERRIDE;
+    void replaceWithWater(LLSettingsWater::ptr_t other);
 
     static LLSD defaults(const LLSettingsBase::TrackPosition& position = 0.0f);
 
diff --git a/indra/llmessage/lldispatcher.cpp b/indra/llmessage/lldispatcher.cpp
index c40fe0d389..717ef10f70 100644
--- a/indra/llmessage/lldispatcher.cpp
+++ b/indra/llmessage/lldispatcher.cpp
@@ -101,48 +101,70 @@ LLDispatchHandler* LLDispatcher::addHandler(
 
 // static
 bool LLDispatcher::unpackMessage(
-		LLMessageSystem* msg,
-		LLDispatcher::key_t& method,
-		LLUUID& invoice,
-		LLDispatcher::sparam_t& parameters)
+    LLMessageSystem* msg,
+    LLDispatcher::key_t& method,
+    LLUUID& invoice,
+    LLDispatcher::sparam_t& parameters)
 {
-	char buf[MAX_STRING];	/*Flawfinder: ignore*/
-	msg->getStringFast(_PREHASH_MethodData, _PREHASH_Method, method);
-	msg->getUUIDFast(_PREHASH_MethodData, _PREHASH_Invoice, invoice);
-	S32 size;
-	S32 count = msg->getNumberOfBlocksFast(_PREHASH_ParamList);
-	for (S32 i = 0; i < count; ++i)
-	{
-		// we treat the SParam as binary data (since it might be an 
-		// LLUUID in compressed form which may have embedded \0's,)
-		size = msg->getSizeFast(_PREHASH_ParamList, i, _PREHASH_Parameter);
-		if (size >= 0)
-		{
-			msg->getBinaryDataFast(
-				_PREHASH_ParamList, _PREHASH_Parameter,
-				buf, size, i, MAX_STRING-1);
+    char buf[MAX_STRING];	/*Flawfinder: ignore*/
+    msg->getStringFast(_PREHASH_MethodData, _PREHASH_Method, method);
+    msg->getUUIDFast(_PREHASH_MethodData, _PREHASH_Invoice, invoice);
+    S32 size;
+    S32 count = msg->getNumberOfBlocksFast(_PREHASH_ParamList);
+    for (S32 i = 0; i < count; ++i)
+    {
+        // we treat the SParam as binary data (since it might be an 
+        // LLUUID in compressed form which may have embedded \0's,)
+        size = msg->getSizeFast(_PREHASH_ParamList, i, _PREHASH_Parameter);
+        if (size >= 0)
+        {
+            msg->getBinaryDataFast(
+                _PREHASH_ParamList, _PREHASH_Parameter,
+                buf, size, i, MAX_STRING - 1);
 
-			// If the last byte of the data is 0x0, this is either a normally
-			// packed string, or a binary packed UUID (which for these messages
-			// are packed with a 17th byte 0x0).  Unpack into a std::string
-			// without the trailing \0, so "abc\0" becomes std::string("abc", 3)
-			// which matches const char* "abc".
-			if (size > 0
-				&& buf[size-1] == 0x0)
-			{
-				// special char*/size constructor because UUIDs may have embedded
-				// 0x0 bytes.
-				std::string binary_data(buf, size-1);
-				parameters.push_back(binary_data);
-			}
-			else
-			{
-				// This is either a NULL string, or a string that was packed 
-				// incorrectly as binary data, without the usual trailing '\0'.
-				std::string string_data(buf, size);
-				parameters.push_back(string_data);
-			}
-		}
-	}
-	return true;
+            // If the last byte of the data is 0x0, this is either a normally
+            // packed string, or a binary packed UUID (which for these messages
+            // are packed with a 17th byte 0x0).  Unpack into a std::string
+            // without the trailing \0, so "abc\0" becomes std::string("abc", 3)
+            // which matches const char* "abc".
+            if (size > 0
+                && buf[size - 1] == 0x0)
+            {
+                // special char*/size constructor because UUIDs may have embedded
+                // 0x0 bytes.
+                std::string binary_data(buf, size - 1);
+                parameters.push_back(binary_data);
+            }
+            else
+            {
+                // This is either a NULL string, or a string that was packed 
+                // incorrectly as binary data, without the usual trailing '\0'.
+                std::string string_data(buf, size);
+                parameters.push_back(string_data);
+            }
+        }
+    }
+    return true;
+}
+
+// static
+bool LLDispatcher::unpackLargeMessage(
+    LLMessageSystem* msg,
+    LLDispatcher::key_t& method,
+    LLUUID& invoice,
+    LLDispatcher::sparam_t& parameters)
+{
+    msg->getStringFast(_PREHASH_MethodData, _PREHASH_Method, method);
+    msg->getUUIDFast(_PREHASH_MethodData, _PREHASH_Invoice, invoice);
+    S32 count = msg->getNumberOfBlocksFast(_PREHASH_ParamList);
+    for (S32 i = 0; i < count; ++i)
+    {
+        // This method treats all Parameter List params as strings and unpacks
+        // them regardless of length. If there is binary data it is the callers 
+        // responsibility to decode it.
+        std::string param;
+        msg->getStringFast(_PREHASH_ParamList, _PREHASH_Parameter, param, i);
+        parameters.push_back(param);
+    }
+    return true;
 }
diff --git a/indra/llmessage/lldispatcher.h b/indra/llmessage/lldispatcher.h
index 9d1751f588..43c63ac4df 100644
--- a/indra/llmessage/lldispatcher.h
+++ b/indra/llmessage/lldispatcher.h
@@ -105,6 +105,12 @@ public:
 		LLUUID& invoice,
 		sparam_t& parameters);
 
+    static bool unpackLargeMessage(
+        LLMessageSystem* msg,
+        key_t& method,
+        LLUUID& invoice,
+        sparam_t& parameters);
+
 protected:
 	typedef std::map<key_t, LLDispatchHandler*> dispatch_map_t;
 	dispatch_map_t mHandlers;
diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp
index ed2d580d48..fba5b7453d 100644
--- a/indra/llmessage/message_prehash.cpp
+++ b/indra/llmessage/message_prehash.cpp
@@ -1394,3 +1394,4 @@ char const* const _PREHASH_AppearanceHover = LLMessageStringTable::getInstance()
 char const* const _PREHASH_HoverHeight = LLMessageStringTable::getInstance()->getString("HoverHeight");
 char const* const _PREHASH_Experience = LLMessageStringTable::getInstance()->getString("Experience");
 char const* const _PREHASH_ExperienceID = LLMessageStringTable::getInstance()->getString("ExperienceID");
+char const* const _PREHASH_LargeGenericMessage = LLMessageStringTable::getInstance()->getString("LargeGenericMessage");
diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h
index d7eb04df57..4f72c01ddf 100644
--- a/indra/llmessage/message_prehash.h
+++ b/indra/llmessage/message_prehash.h
@@ -1394,4 +1394,6 @@ extern char const* const _PREHASH_AppearanceHover;
 extern char const* const _PREHASH_HoverHeight;
 extern char const* const _PREHASH_Experience;
 extern char const* const _PREHASH_ExperienceID;
+extern char const* const _PREHASH_LargeGenericMessage;
+
 #endif
diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp
index 3a030bb09d..fa3afa74ca 100644
--- a/indra/newview/llenvironment.cpp
+++ b/indra/newview/llenvironment.cpp
@@ -280,7 +280,6 @@ namespace
         virtual bool operator()(const LLDispatcher *, const std::string& key, const LLUUID& invoice, const sparam_t& strings) override
         {
             LLSD message;
-
             sparam_t::const_iterator it = strings.begin();
 
             if (it != strings.end())
@@ -292,8 +291,8 @@ namespace
                     LL_WARNS() << "LLExperienceLogDispatchHandler: Attempted to read parameter data into LLSD but failed:" << llsdRaw << LL_ENDL;
                 }
             }
-            message[KEY_EXPERIENCEID] = invoice;
 
+            message[KEY_EXPERIENCEID] = invoice;
             // Object Name
             if (it != strings.end())
             {
@@ -349,7 +348,7 @@ void LLEnvironment::initSingleton()
     LLSettingsSky::ptr_t p_default_sky = LLSettingsVOSky::buildDefaultSky();
     LLSettingsWater::ptr_t p_default_water = LLSettingsVOWater::buildDefaultWater();
 
-    mCurrentEnvironment = std::make_shared<DayInstance>();
+    mCurrentEnvironment = std::make_shared<DayInstance>(ENV_DEFAULT);
     mCurrentEnvironment->setSky(p_default_sky);
     mCurrentEnvironment->setWater(p_default_water);
 
@@ -381,6 +380,43 @@ bool LLEnvironment::canEdit() const
     return true;
 }
 
+LLSettingsSky::ptr_t LLEnvironment::getCurrentSky() const 
+{ 
+    LLSettingsSky::ptr_t psky = mCurrentEnvironment->getSky(); 
+
+    if (!psky && mCurrentEnvironment->getEnvironmentSelection() >= ENV_EDIT)
+    {
+        for (int idx = 0; idx < ENV_END; ++idx)
+        {
+            if (mEnvironments[idx]->getSky())
+            {
+                psky = mEnvironments[idx]->getSky();
+                break;
+            }
+        }
+    }
+    return psky;
+}
+
+LLSettingsWater::ptr_t LLEnvironment::getCurrentWater() const 
+{
+    LLSettingsWater::ptr_t pwater = mCurrentEnvironment->getWater(); 
+
+    if (!pwater && mCurrentEnvironment->getEnvironmentSelection() >= ENV_EDIT)
+    {
+        for (int idx = 0; idx < ENV_END; ++idx)
+        {
+            if (mEnvironments[idx]->getWater())
+            {
+                pwater = mEnvironments[idx]->getWater();
+                break;
+            }
+        }
+    }
+    return pwater;
+}
+
+
 void LLEnvironment::getAtmosphericModelSettings(AtmosphericModelSettings& settingsOut, const LLSettingsSky::ptr_t &psky)
 {
     settingsOut.m_skyBottomRadius   = psky->getSkyBottomRadius();
@@ -478,7 +514,7 @@ bool LLEnvironment::isInventoryEnabled() const
 
 void LLEnvironment::onRegionChange()
 {
-    clearEnvironment(ENV_PUSH);
+    clearExperienceEnvironment(LLUUID::null, TRANSITION_DEFAULT);
     requestRegion();
 }
 
@@ -540,10 +576,16 @@ bool LLEnvironment::hasEnvironment(LLEnvironment::EnvSelection_t env)
 LLEnvironment::DayInstance::ptr_t LLEnvironment::getEnvironmentInstance(LLEnvironment::EnvSelection_t env, bool create /*= false*/)
 {
     DayInstance::ptr_t environment = mEnvironments[env];
-//    if (!environment && create)
     if (create)
     {
-        environment = std::make_shared<DayInstance>();
+        if (environment)
+            environment = environment->clone();
+        else
+        {
+            environment = std::make_shared<DayInstance>(env);
+            if (mMakeBackups && env > ENV_PUSH)
+                environment->setBackup(true);
+        }
         mEnvironments[env] = environment;
     }
 
@@ -581,17 +623,27 @@ void LLEnvironment::setEnvironment(LLEnvironment::EnvSelection_t env, LLEnvironm
 
     DayInstance::ptr_t environment = getEnvironmentInstance(env, true);
 
-    LLSettingsSky::ptr_t prev_sky = mEnvironments[ENV_DEFAULT]->getSky();
-    LLSettingsWater::ptr_t prev_water = mEnvironments[ENV_DEFAULT]->getWater();
-    if (mCurrentEnvironment && (ENV_EDIT == env))
-    {
-        prev_sky = mCurrentEnvironment->getSky() ? mCurrentEnvironment->getSky() : prev_sky;
-        prev_water = mCurrentEnvironment->getWater() ? mCurrentEnvironment->getWater() : prev_water;
-    }
+//     LLSettingsSky::ptr_t prev_sky = mEnvironments[ENV_DEFAULT]->getSky();
+//     LLSettingsWater::ptr_t prev_water = mEnvironments[ENV_DEFAULT]->getWater();
+//     if (mCurrentEnvironment && (ENV_EDIT == env))
+//     {
+//         prev_sky = mCurrentEnvironment->getSky() ? mCurrentEnvironment->getSky() : prev_sky;
+//         prev_water = mCurrentEnvironment->getWater() ? mCurrentEnvironment->getWater() : prev_water;
+//     }
 
-    environment->clear();
-    environment->setSky((fixed.first) ? fixed.first : prev_sky);
-    environment->setWater((fixed.second) ? fixed.second : prev_water);
+//     environment->clear();
+//     environment->setSky((fixed.first) ? fixed.first : prev_sky);
+//     environment->setWater((fixed.second) ? fixed.second : prev_water);
+    if (fixed.first)
+        environment->setSky(fixed.first);
+    else if (!environment->getSky())
+        environment->setSky(mCurrentEnvironment->getSky());
+        
+    if (fixed.second)
+        environment->setWater(fixed.second);
+    else if (!environment->getWater())
+        environment->setWater(mCurrentEnvironment->getWater());
+        

     if (!mSignalEnvChanged.empty())
@@ -630,19 +682,19 @@ void LLEnvironment::setEnvironment(LLEnvironment::EnvSelection_t env, const LLSe
     else if (settings->getSettingsType() == "sky")
     {
         fixedEnvironment_t fixedenv(std::static_pointer_cast<LLSettingsSky>(settings), LLSettingsWater::ptr_t());
-        if (environment)
-        {
-            fixedenv.second = environment->getWater();
-        }
+//         if (environment)
+//         {
+//             fixedenv.second = environment->getWater();
+//         }
         setEnvironment(env, fixedenv);
     }
     else if (settings->getSettingsType() == "water")
     {
         fixedEnvironment_t fixedenv(LLSettingsSky::ptr_t(), std::static_pointer_cast<LLSettingsWater>(settings));
-        if (environment)
-        {
-            fixedenv.first = environment->getSky();
-        }
+//         if (environment)
+//         {
+//             fixedenv.first = environment->getSky();
+//         }
         setEnvironment(env, fixedenv);
     }
 }
@@ -794,6 +846,17 @@ LLEnvironment::DayInstance::ptr_t LLEnvironment::getSelectedEnvironmentInstance(
     return mEnvironments[ENV_DEFAULT];
 }
 
+LLEnvironment::DayInstance::ptr_t LLEnvironment::getSharedEnvironmentInstance()
+{
+    for (S32 idx = ENV_PARCEL; idx < ENV_DEFAULT; ++idx)
+    {
+        if (mEnvironments[idx])
+            return mEnvironments[idx];
+    }
+
+    return mEnvironments[ENV_DEFAULT];
+}
+
 void LLEnvironment::updateEnvironment(LLSettingsBase::Seconds transition, bool forced)
 {
     DayInstance::ptr_t pinstance = getSelectedEnvironmentInstance();
@@ -920,6 +983,11 @@ void LLEnvironment::update(const LLViewerCamera * cam)
 
     mCurrentEnvironment->applyTimeDelta(delta);
 
+    if (mCurrentEnvironment->getEnvironmentSelection() != ENV_LOCAL)
+    {
+        applyInjectedSettings(mCurrentEnvironment, delta);
+    }
+
     // update clouds, sun, and general
     updateCloudScroll();
 
@@ -1703,32 +1771,37 @@ void LLEnvironment::handleEnvironmentPushFull(LLUUID experience_id, LLSD &messag
 
 void LLEnvironment::handleEnvironmentPushPartial(LLUUID experience_id, LLSD &message, F32 transition)
 {
+    LLSD settings(message["settings"]);
+
+    if (settings.isUndefined())
+        return;
 
+    setExperienceEnvironment(experience_id, settings, LLSettingsBase::Seconds(transition));
 }
 
 void LLEnvironment::clearExperienceEnvironment(LLUUID experience_id, F32 transition_time)
 {
     bool update_env(false);
 
-    if (mPushEnvironmentExpId == experience_id)
+    if (hasEnvironment(ENV_PUSH))
     {
-        mPushEnvironmentExpId.setNull();
-
-        if (hasEnvironment(ENV_PUSH))
-        {
-            update_env |= true;
-            clearEnvironment(ENV_PUSH);
-            updateEnvironment(LLSettingsBase::Seconds(transition_time));
-        }
-
+        update_env |= true;
+        clearEnvironment(ENV_PUSH);
+        updateEnvironment(LLSettingsBase::Seconds(transition_time));
     }
 
-    // clear the override queue too.
-    // update |= true;
+    setInstanceBackup(false);
 
+    /*TODO blend these back out*/
+    mSkyExperienceBlends.clear();
+    mWaterExperienceBlends.clear();
+    mCurrentEnvironment->getSky();
 
-    if (update_env)
-        updateEnvironment(LLSettingsBase::Seconds(transition_time));
+    injectSettings(experience_id, mSkyExperienceBlends, mSkyOverrides, LLSettingsBase::Seconds(transition_time), false);
+    injectSettings(experience_id, mWaterExperienceBlends, mWaterOverrides, LLSettingsBase::Seconds(transition_time), false);
+
+    mSkyOverrides = LLSD::emptyMap();
+    mWaterOverrides = LLSD::emptyMap();
 }
 
 void LLEnvironment::setSharedEnvironment()
@@ -1754,10 +1827,110 @@ void LLEnvironment::setExperienceEnvironment(LLUUID experience_id, LLUUID asset_
 
 void LLEnvironment::setExperienceEnvironment(LLUUID experience_id, LLSD data, F32 transition_time)
 {
+    LLSD sky(data["sky"]);
+    LLSD water(data["water"]);
+
+    if (sky.isUndefined() && water.isUndefined())
+    {
+        clearExperienceEnvironment(experience_id, transition_time);
+        return;
+    }
+
+    setInstanceBackup(true);
+
+    if (!sky.isUndefined())
+        injectSettings(experience_id, mSkyExperienceBlends, sky, LLSettingsBase::Seconds(transition_time), true);
+    if (!water.isUndefined())
+        injectSettings(experience_id, mWaterExperienceBlends, water, LLSettingsBase::Seconds(transition_time), true);
+
+}
+
+
+void LLEnvironment::setInstanceBackup(bool dobackup)
+{
+    mMakeBackups = dobackup;
+    for (S32 idx = ENV_PARCEL; idx < ENV_DEFAULT; ++idx)
+    {
+        if (mEnvironments[idx])
+            mEnvironments[idx]->setBackup(dobackup);
+    }
+}
+
+void LLEnvironment::injectSettings(LLUUID experience_id, exerienceBlendValues_t &blends, LLSD injections, LLSettingsBase::Seconds transition, bool blendin)
+{
+    for (LLSD::map_iterator it = injections.beginMap(); it != injections.endMap(); ++it)
+    {
+        blends.push_back(ExpBlendValue(transition, (*it).first, (*it).second, blendin, -1));
+    }
+
+    std::stable_sort(blends.begin(), blends.end(), [](const ExpBlendValue &a, const ExpBlendValue &b) { return a.mTimeRemaining < b.mTimeRemaining; });
+}
+
+void LLEnvironment::applyInjectedSettings(DayInstance::ptr_t environment, F32Seconds delta)
+{
+    if ((mSkyOverrides.size() > 0) || (mSkyExperienceBlends.size() > 0))
+    {
+        LLSettingsSky::ptr_t psky = environment->getSky();
+        applyInjectedValues(psky, mSkyOverrides);
+        blendInjectedValues(psky, mSkyExperienceBlends, mSkyOverrides, delta);
+    }
+    if ((mWaterOverrides.size() > 0) || (mWaterExperienceBlends.size() > 0))
+    {
+        LLSettingsWater::ptr_t pwater = environment->getWater();
+        applyInjectedValues(pwater, mWaterOverrides);
+        blendInjectedValues(pwater, mWaterExperienceBlends, mWaterOverrides, delta);
+    }
+}
+
+
+void LLEnvironment::applyInjectedValues(LLSettingsBase::ptr_t psetting, LLSD injection)
+{
+    for (LLSD::map_iterator it = injection.beginMap(); it != injection.endMap(); ++it)
+    {
+        psetting->setValue((*it).first, (*it).second);
+    }
+}
+
+void LLEnvironment::blendInjectedValues(LLSettingsBase::ptr_t psetting, exerienceBlendValues_t &blends, LLSD &overrides, F32Seconds delta)
+{
+    LLSD settings = psetting->getSettings();
+    LLSettingsBase::parammapping_t mappings = psetting->getParameterMap();
+    LLSettingsBase::stringset_t    slerps = psetting->getSlerpKeys();
+
+    if (blends.empty())
+        return;
+
+    for (auto &blend : blends)
+    {
+        blend.mTimeRemaining -= delta;
+        LLSettingsBase::BlendFactor mix = std::max(blend.mTimeRemaining / blend.mTransition, 0.0f);
+        if (blend.mBlendIn)
+            mix = 1.0 - mix;
+        mix = std::max(0.0, std::min(mix, 1.0));
+
+        if (blend.mValueInitial.isUndefined())
+            blend.mValueInitial = psetting->getValue(blend.mKeyName);
+        LLSD newvalue = psetting->interpolateSDValue(blend.mKeyName, blend.mValueInitial, blend.mValue, mappings, mix, slerps);
+
+        psetting->setValue(blend.mKeyName, newvalue);
+    }
+    
+    auto it = blends.begin();
+    for (; it != blends.end(); ++it)
+    {
+        if ((*it).mTimeRemaining > F32Seconds(0.0f))
+            break;
+        if ((*it).mBlendIn)
+            overrides[(*it).mKeyName] = (*it).mValue;
+    }
+    if (it != blends.begin())
+    {
+        blends.erase(blends.begin(), it);
+    }
 }
 
 //=========================================================================
-LLEnvironment::DayInstance::DayInstance() :
+LLEnvironment::DayInstance::DayInstance(EnvSelection_t env) :
     mDayCycle(),
     mSky(),
     mWater(),
@@ -1767,18 +1940,42 @@ LLEnvironment::DayInstance::DayInstance() :
     mBlenderWater(),
     mInitialized(false),
     mType(TYPE_INVALID),
-    mSkyTrack(1)
+    mSkyTrack(1),
+    mEnv(env),
+    mBackup(false)
 { }
 
+
+LLEnvironment::DayInstance::ptr_t LLEnvironment::DayInstance::clone() const
+{
+    ptr_t environment = std::make_shared<DayInstance>(mEnv);
+
+    environment->mDayCycle = mDayCycle;
+    environment->mSky = mSky;
+    environment->mWater = mWater;
+    environment->mDayLength = mDayLength;
+    environment->mDayOffset = mDayOffset;
+    environment->mBlenderSky = mBlenderSky;
+    environment->mBlenderWater = mBlenderWater;
+    environment->mInitialized = mInitialized;
+    environment->mType = mType;
+    environment->mSkyTrack = mSkyTrack;
+
+    return environment;
+}
+
 void LLEnvironment::DayInstance::applyTimeDelta(const LLSettingsBase::Seconds& delta)
 {
+    bool changed(false);
     if (!mInitialized)
         initialize();
 
     if (mBlenderSky)
-        mBlenderSky->applyTimeDelta(delta);
+        changed |= mBlenderSky->applyTimeDelta(delta);
     if (mBlenderWater)
-        mBlenderWater->applyTimeDelta(delta);
+        changed |= mBlenderWater->applyTimeDelta(delta);
+    if (mBackup && changed)
+        backup();
 }
 
 void LLEnvironment::DayInstance::setDay(const LLSettingsDay::ptr_t &pday, LLSettingsDay::Seconds daylength, LLSettingsDay::Seconds dayoffset)
@@ -1815,6 +2012,8 @@ void LLEnvironment::DayInstance::setSky(const LLSettingsSky::ptr_t &psky)
     mSky->mReplaced |= different_sky;
     mSky->update();
     mBlenderSky.reset();
+    if (mBackup)
+        backup();
 
     if (gAtmosphere)
     {
@@ -1834,6 +2033,8 @@ void LLEnvironment::DayInstance::setWater(const LLSettingsWater::ptr_t &pwater)
     mWater = pwater;
     mWater->update();
     mBlenderWater.reset();
+    if (mBackup)
+        backup();
 }
 
 void LLEnvironment::DayInstance::initialize()
@@ -1875,6 +2076,53 @@ void LLEnvironment::DayInstance::setBlenders(const LLSettingsBlender::ptr_t &sky
     mBlenderWater = waterblend;
 }
 
+
+void LLEnvironment::DayInstance::setBackup(bool backupval)
+{
+    if (backupval == mBackup)
+        return;
+
+    mBackup = backupval;
+    LLSettingsSky::ptr_t psky = getSky();
+    if (mBackup)
+    {
+        backup();
+    }
+    else
+    {
+        restore();
+        mBackupSky = LLSD();
+        mBackupWater = LLSD();
+    }
+}
+
+void LLEnvironment::DayInstance::backup()
+{
+    if (!mBackup)
+        return;
+
+    if (mSky)
+    {
+        mBackupSky = mSky->cloneSettings();
+    }
+    if (mWater)
+    {
+        mBackupWater = mWater->cloneSettings();
+    }
+}
+
+void LLEnvironment::DayInstance::restore()
+{
+    if (!mBackupSky.isUndefined() && mSky)
+    {
+        mSky->replaceSettings(mBackupSky);
+    }
+    if (!mBackupWater.isUndefined() && mWater)
+    {
+        mWater->replaceSettings(mBackupWater);
+    }
+}
+
 LLSettingsBase::TrackPosition LLEnvironment::DayInstance::secondsToKeyframe(LLSettingsDay::Seconds seconds)
 {
     return convert_time_to_position(seconds, mDayLength);
@@ -1923,7 +2171,7 @@ void LLEnvironment::DayInstance::animate()
 //-------------------------------------------------------------------------
 LLEnvironment::DayTransition::DayTransition(const LLSettingsSky::ptr_t &skystart,
     const LLSettingsWater::ptr_t &waterstart, LLEnvironment::DayInstance::ptr_t &end, LLSettingsDay::Seconds time) :
-    DayInstance(),
+    DayInstance(ENV_NONE),
     mStartSky(skystart),
     mStartWater(waterstart),
     mNextInstance(end),
@@ -2038,3 +2286,5 @@ F64 LLTrackBlenderLoopingManual::getSpanLength(const LLSettingsDay::TrackBound_t
 {
     return get_wrapping_distance((*bounds.first).first, (*bounds.second).first);
 }
+
+//=========================================================================
diff --git a/indra/newview/llenvironment.h b/indra/newview/llenvironment.h
index 7e35fdcfac..613bcdb4f8 100644
--- a/indra/newview/llenvironment.h
+++ b/indra/newview/llenvironment.h
@@ -121,8 +121,8 @@ public:
     bool                        canAgentUpdateRegionEnvironment() const;
 
     LLSettingsDay::ptr_t        getCurrentDay() const { return mCurrentEnvironment->getDayCycle(); }
-    LLSettingsSky::ptr_t        getCurrentSky() const { return mCurrentEnvironment->getSky(); }
-    LLSettingsWater::ptr_t      getCurrentWater() const { return mCurrentEnvironment->getWater(); }
+    LLSettingsSky::ptr_t        getCurrentSky() const;
+    LLSettingsWater::ptr_t      getCurrentWater() const;
 
     static void                 getAtmosphericModelSettings(AtmosphericModelSettings& settingsOut, const LLSettingsSky::ptr_t &psky);
 
@@ -218,13 +218,6 @@ public:
 
     void                        handleEnvironmentPush(LLSD &message);
 
-protected:
-    virtual void                initSingleton();
-
-private:
-    LLVector4 toCFR(const LLVector3 vec) const;
-    LLVector4 toLightNorm(const LLVector3 vec) const;
-
     class DayInstance
     {
     public:
@@ -236,14 +229,16 @@ private:
         };
         typedef std::shared_ptr<DayInstance> ptr_t;
 
-                                    DayInstance();
+                                    DayInstance(EnvSelection_t env);
         virtual                     ~DayInstance() { };
 
+        ptr_t                       clone() const;
+
         virtual void                applyTimeDelta(const LLSettingsBase::Seconds& delta);
 
-        void                        setDay(const LLSettingsDay::ptr_t &pday, LLSettingsDay::Seconds daylength, LLSettingsDay::Seconds dayoffset);
-        void                        setSky(const LLSettingsSky::ptr_t &psky);
-        void                        setWater(const LLSettingsWater::ptr_t &pwater);
+        virtual void                setDay(const LLSettingsDay::ptr_t &pday, LLSettingsDay::Seconds daylength, LLSettingsDay::Seconds dayoffset);
+        virtual void                setSky(const LLSettingsSky::ptr_t &psky);
+        virtual void                setWater(const LLSettingsWater::ptr_t &pwater);
 
         void                        initialize();
         bool                        isInitialized();
@@ -263,12 +258,24 @@ private:
 
         void                        setBlenders(const LLSettingsBlender::ptr_t &skyblend, const LLSettingsBlender::ptr_t &waterblend);
 
+        EnvSelection_t              getEnvironmentSelection() const { return mEnv; }
+
+        void                        setBackup(bool backup);
+        bool                        getBackup() const       { return mBackup; }
+        bool                        hasBackupSky() const    { return !mBackupSky.isUndefined() || !mBackupWater.isUndefined(); }
+        void                        backup();
+        void                        restore();
+
     protected:
         LLSettingsDay::ptr_t        mDayCycle;
         LLSettingsSky::ptr_t        mSky;
         LLSettingsWater::ptr_t      mWater;
         S32                         mSkyTrack;
 
+        bool                        mBackup;
+        LLSD                        mBackupSky;
+        LLSD                        mBackupWater;
+
         InstanceType_t              mType;
         bool                        mInitialized;
 
@@ -279,8 +286,21 @@ private:
         LLSettingsBlender::ptr_t    mBlenderSky;
         LLSettingsBlender::ptr_t    mBlenderWater;
 
+        EnvSelection_t              mEnv;
+
         LLSettingsBase::TrackPosition secondsToKeyframe(LLSettingsDay::Seconds seconds);
     };
+
+    DayInstance::ptr_t          getSelectedEnvironmentInstance();
+    DayInstance::ptr_t          getSharedEnvironmentInstance();
+
+protected:
+    virtual void                initSingleton();
+
+private:
+    LLVector4 toCFR(const LLVector3 vec) const;
+    LLVector4 toLightNorm(const LLVector3 vec) const;
+
     typedef std::array<DayInstance::ptr_t, ENV_END> InstanceArray_t;
 
     struct ExpEnvironmentEntry
@@ -334,9 +354,12 @@ private:
     S32                         mCurrentTrack;
     altitude_list_t             mTrackAltitudes;
 
-    DayInstance::ptr_t          getEnvironmentInstance(EnvSelection_t env, bool create = false);
+    LLSD                        mSkyOverrides;
+    LLSD                        mWaterOverrides;
+    LLSD                        mSkyOverrideBlends;
+    LLSD                        mWaterOverrideBlends;
 
-    DayInstance::ptr_t          getSelectedEnvironmentInstance();
+    DayInstance::ptr_t          getEnvironmentInstance(EnvSelection_t env, bool create = false);
 
     void                        updateCloudScroll();
 
@@ -373,6 +396,31 @@ private:
         std::string             mDayName;
     };
 
+    struct ExpBlendValue
+    {
+        ExpBlendValue(F32Seconds transition, const std::string &keyname, LLSD value, bool blendin, S32 index = -1) :
+            mTransition(transition),
+            mTimeRemaining(transition),
+            mKeyName(keyname),
+            mValue(value),
+            mValueInitial(),
+            mIndex(index),
+            mBlendIn(blendin)
+        {}
+
+        F32Seconds      mTransition;
+        F32Seconds      mTimeRemaining;
+        std::string     mKeyName;
+        LLSD            mValue;
+        LLSD            mValueInitial;
+        S32             mIndex;
+        bool            mBlendIn;
+
+        typedef std::shared_ptr<ExpBlendValue>  ptr_t;
+    };
+
+    typedef std::deque<ExpBlendValue>   exerienceBlendValues_t;
+
     void                        coroRequestEnvironment(S32 parcel_id, environment_apply_fn apply);
     void                        coroUpdateEnvironment(S32 parcel_id, S32 track_no, UpdateInfo::ptr_t updates, environment_apply_fn apply);
     void                        coroResetEnvironment(S32 parcel_id, S32 track_no, environment_apply_fn apply);
@@ -391,6 +439,17 @@ private:
     void                        clearExperienceEnvironment(LLUUID experience_id, F32 transition_time);
     void                        setExperienceEnvironment(LLUUID experience_id, LLUUID asset_id, F32 transition_time);
     void                        setExperienceEnvironment(LLUUID experience_id, LLSD environment, F32 transition_time);
+    void                        setInstanceBackup(bool dobackup);
+
+    void injectSettings(LLUUID experience_id, exerienceBlendValues_t &blends, LLSD injections, LLSettingsBase::Seconds transition, bool blendin);
+
+    void                        applyInjectedSettings(DayInstance::ptr_t environment, F32Seconds delta);
+    void                        applyInjectedValues(LLSettingsBase::ptr_t psetting, LLSD injection);
+    void                        blendInjectedValues(LLSettingsBase::ptr_t psetting, exerienceBlendValues_t &blends, LLSD &overrides, F32Seconds delta);
+
+    exerienceBlendValues_t      mSkyExperienceBlends;
+    exerienceBlendValues_t      mWaterExperienceBlends;
+    bool                        mMakeBackups;
 };
 
 class LLTrackBlenderLoopingManual : public LLSettingsBlender
diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp
index 5e0f3ab7f9..299fffd9ab 100644
--- a/indra/newview/lleventpoll.cpp
+++ b/indra/newview/lleventpoll.cpp
@@ -106,6 +106,7 @@ namespace Details
         LLSD message;
         message["sender"] = mSenderIp;
         message["body"] = content["body"];
+
         LLMessageSystem::dispatch(msg_name, message);
     }
 
@@ -241,7 +242,7 @@ namespace Details
                 !result.get("events") ||
                 !result.get("id"))
             {
-                LL_WARNS("LLEventPollImpl") << " <" << counter << "> received event poll with no events or id key: " << LLSDXMLStreamer(result) << LL_ENDL;
+                LL_WARNS("LLEventPollImpl") << " <" << counter << "> received event poll with no events or id key: " << result << LL_ENDL;
                 continue;
             }
 
@@ -254,7 +255,7 @@ namespace Details
             }
 
             // was LL_INFOS() but now that CoarseRegionUpdate is TCP @ 1/second, it'd be too verbose for viewer logs. -MG
-            LL_DEBUGS("LLEventPollImpl") << " <" << counter << "> " << events.size() << "events (id " << LLSDXMLStreamer(acknowledge) << ")" << LL_ENDL;
+            LL_DEBUGS("LLEventPollImpl") << " <" << counter << "> " << events.size() << "events (id " << acknowledge << ")" << LL_ENDL;
 
             LLSD::array_const_iterator i = events.beginArray();
             LLSD::array_const_iterator end = events.endArray();
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index a9a0b89078..9c0e842c30 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -2532,7 +2532,8 @@ void register_viewer_callbacks(LLMessageSystem* msg)
 
 	msg->setHandlerFunc("InitiateDownload", process_initiate_download);
 	msg->setHandlerFunc("LandStatReply", LLFloaterTopObjects::handle_land_reply);
-	msg->setHandlerFunc("GenericMessage", process_generic_message);
+    msg->setHandlerFunc("GenericMessage", process_generic_message);
+    msg->setHandlerFunc("LargeGenericMessage", process_large_generic_message);
 
 	msg->setHandlerFuncFast(_PREHASH_FeatureDisabled, process_feature_disabled_message);
 }
diff --git a/indra/newview/llviewergenericmessage.cpp b/indra/newview/llviewergenericmessage.cpp
index 3df53a4a30..d3de9d72bf 100644
--- a/indra/newview/llviewergenericmessage.cpp
+++ b/indra/newview/llviewergenericmessage.cpp
@@ -70,8 +70,6 @@ void send_generic_message(const std::string& method,
 	gAgent.sendReliableMessage();
 }
 
-
-
 void process_generic_message(LLMessageSystem* msg, void**)
 {
 	LLUUID agent_id;
@@ -93,3 +91,25 @@ void process_generic_message(LLMessageSystem* msg, void**)
 			<< LL_ENDL;
 	}
 }
+
+void process_large_generic_message(LLMessageSystem* msg, void**)
+{
+    LLUUID agent_id;
+    msg->getUUID("AgentData", "AgentID", agent_id);
+    if (agent_id != gAgent.getID())
+    {
+        LL_WARNS() << "GenericMessage for wrong agent" << LL_ENDL;
+        return;
+    }
+
+    std::string request;
+    LLUUID invoice;
+    LLDispatcher::sparam_t strings;
+    LLDispatcher::unpackLargeMessage(msg, request, invoice, strings);
+
+    if (!gGenericDispatcher.dispatch(request, invoice, strings))
+    {
+        LL_WARNS() << "GenericMessage " << request << " failed to dispatch"
+            << LL_ENDL;
+    }
+}
diff --git a/indra/newview/llviewergenericmessage.h b/indra/newview/llviewergenericmessage.h
index 9d451ec0bc..170f38a485 100644
--- a/indra/newview/llviewergenericmessage.h
+++ b/indra/newview/llviewergenericmessage.h
@@ -38,6 +38,7 @@ void send_generic_message(const std::string& method,
 						  const LLUUID& invoice = LLUUID::null);
 
 void process_generic_message(LLMessageSystem* msg, void**);
+void process_large_generic_message(LLMessageSystem* msg, void**);

 extern LLDispatcher gGenericDispatcher;
diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg
index ed32804bef..635227ccf3 100755
--- a/scripts/messages/message_template.msg
+++ b/scripts/messages/message_template.msg
@@ -6,7 +6,7 @@ version 2.0
 // numbers. Each message must be numbered relative to the
 // other messages of that type. The current highest number 
 // for each type is listed below:
-// Low: 423
+// Low: 430
 // Medium: 18
 // High: 30
 // PLEASE UPDATE THIS WHEN YOU ADD A NEW MESSAGE!
@@ -5780,6 +5780,28 @@ version 2.0
 	}
 }
 
+// LargeGenericMessage
+// Similar to the above messages, but can handle larger payloads and serialized 
+// LLSD.  Uses HTTP transport 
+{
+    LargeGenericMessage Low 430 NotTrusted Unencoded UDPDeprecated
+    {
+		AgentData 		Single
+		{   AgentID     LLUUID  	}
+		{	SessionID	LLUUID		}
+		{	TransactionID	LLUUID	}
+    }
+    {
+        MethodData Single
+		{	Method		Variable 1 }
+		{	Invoice		LLUUID	}
+    }
+    {
+		ParamList		Variable
+		{	Parameter	Variable 2 }
+    }
+}
+
 // ***************************************************************************
 // Requests for possessions, acquisition, money, etc
 // ***************************************************************************
diff --git a/scripts/messages/message_template.msg.sha1 b/scripts/messages/message_template.msg.sha1
index c9ef0217f3..bf45b08f93 100755
--- a/scripts/messages/message_template.msg.sha1
+++ b/scripts/messages/message_template.msg.sha1
@@ -1 +1 @@
-e492dec0fcdb4e234f94ddc32f4d7af0290ca72b
\ No newline at end of file
+be964beb7a2cd060a438c89fd5cb25e2f687d45e
\ No newline at end of file
-- 
cgit v1.3