From d0deb4b2d75d626e5e73a63519b2a0f6b38259d5 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Fri, 25 Mar 2022 03:48:29 -0400
Subject: [PATCH] Support opensim limits

---
 indra/llmath/xform.h               | 18 ++++++---
 indra/llprimitive/llprimitive.cpp  |  6 ++-
 indra/llprimitive/llprimitive.h    | 38 ++++++++++--------
 indra/newview/llmanipscale.cpp     | 36 +++++++++--------
 indra/newview/llmaniptranslate.cpp |  4 +-
 indra/newview/llmodelpreview.cpp   |  4 +-
 indra/newview/llpanelobject.cpp    | 55 ++++++++++++++++++++-----
 indra/newview/llpanelobject.h      | 17 +++++---
 indra/newview/llselectmgr.cpp      |  6 ++-
 indra/newview/lltoolgrab.cpp       |  4 +-
 indra/newview/llviewerregion.cpp   | 64 +++++++++++++++++++++++++++++-
 indra/newview/llviewerregion.h     | 15 +++++++
 indra/newview/llworld.cpp          | 32 +++++++++++++++
 indra/newview/llworld.h            | 18 ++++++++-
 14 files changed, 251 insertions(+), 66 deletions(-)

diff --git a/indra/llmath/xform.h b/indra/llmath/xform.h
index 03ff645cda2..285726e639a 100644
--- a/indra/llmath/xform.h
+++ b/indra/llmath/xform.h
@@ -31,12 +31,18 @@
 #include "llmatrix4a.h"
 #include "llquaternion.h"
 
-const F32 MAX_OBJECT_Z 		= 4096.f; // should match REGION_HEIGHT_METERS, Pre-havok4: 768.f
-const F32 MIN_OBJECT_Z 		= -256.f;
-const F32 DEFAULT_MAX_PRIM_SCALE = 64.f;
-const F32 DEFAULT_MAX_PRIM_SCALE_NO_MESH = 10.f;
-const F32 MIN_PRIM_SCALE = 0.01f;
-const F32 MAX_PRIM_SCALE = 65536.f;	// something very high but not near FLT_MAX
+constexpr F32 SL_MAX_OBJECT_Z 		= 4096.f;
+constexpr F32 SL_MIN_OBJECT_Z 		= -256.f;
+constexpr F32 SL_DEFAULT_MAX_PRIM_SCALE = 64.f;
+constexpr F32 SL_DEFAULT_MAX_PRIM_SCALE_NO_MESH = 10.f;
+constexpr F32 SL_MIN_PRIM_SCALE = 0.01f;
+
+constexpr F32 OS_MAX_OBJECT_Z 		= 10000.f;
+constexpr F32 OS_MIN_OBJECT_Z 		= -10000.f;
+constexpr F32 OS_DEFAULT_MAX_PRIM_SCALE = 256.f;
+constexpr F32 OS_MIN_PRIM_SCALE = 0.001f;
+
+constexpr F32 MAX_PRIM_SCALE = 65536.f;	// something very high but not near FLT_MAX
 
 class LLXform
 {
diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp
index 149cf9f93e1..965991c3032 100644
--- a/indra/llprimitive/llprimitive.cpp
+++ b/indra/llprimitive/llprimitive.cpp
@@ -60,10 +60,14 @@ const F32 OBJECT_TWIST_LINEAR_MIN	= -180.f;
 const F32 OBJECT_TWIST_LINEAR_MAX	=  180.f;
 const F32 OBJECT_TWIST_LINEAR_INC	=    9.f;
 
-const F32 OBJECT_MIN_HOLE_SIZE = 0.05f;
+const F32 SL_OBJECT_MIN_HOLE_SIZE = 0.05f;
+const F32 OS_OBJECT_MIN_HOLE_SIZE = 0.01f;
 const F32 OBJECT_MAX_HOLE_SIZE_X = 1.0f;
 const F32 OBJECT_MAX_HOLE_SIZE_Y = 0.5f;
 
+const F32 SL_OBJECT_MAX_HOLLOW_SIZE = 95.f;
+const F32 OS_OBJECT_MAX_HOLLOW_SIZE = 99.f;
+
 // Revolutions parameters.
 const F32 OBJECT_REV_MIN = 1.0f;
 const F32 OBJECT_REV_MAX = 4.0f;
diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h
index aa53677adcc..2e3b7b64fcd 100644
--- a/indra/llprimitive/llprimitive.h
+++ b/indra/llprimitive/llprimitive.h
@@ -80,10 +80,14 @@ extern const F32 OBJECT_TWIST_LINEAR_MIN;
 extern const F32 OBJECT_TWIST_LINEAR_MAX;
 extern const F32 OBJECT_TWIST_LINEAR_INC;
 
-extern const F32 OBJECT_MIN_HOLE_SIZE;
+extern const F32 SL_OBJECT_MIN_HOLE_SIZE;
+extern const F32 OS_OBJECT_MIN_HOLE_SIZE;
 extern const F32 OBJECT_MAX_HOLE_SIZE_X;
 extern const F32 OBJECT_MAX_HOLE_SIZE_Y;
 
+extern const F32 SL_OBJECT_MAX_HOLLOW_SIZE;
+extern const F32 OS_OBJECT_MAX_HOLLOW_SIZE;
+
 // Revolutions parameters.
 extern const F32 OBJECT_REV_MIN;
 extern const F32 OBJECT_REV_MAX;
@@ -140,10 +144,10 @@ class LLLightParams final : public LLNetworkData
 
 public:
 	LLLightParams();
-	/*virtual*/ BOOL pack(LLDataPacker &dp) const;
-	/*virtual*/ BOOL unpack(LLDataPacker &dp);
-	/*virtual*/ bool operator==(const LLNetworkData& data) const;
-	/*virtual*/ void copy(const LLNetworkData& data);
+	/*virtual*/ BOOL pack(LLDataPacker &dp) const override;
+	/*virtual*/ BOOL unpack(LLDataPacker &dp) override;
+	/*virtual*/ bool operator==(const LLNetworkData& data) const override;
+	/*virtual*/ void copy(const LLNetworkData& data) override;
 	// LLSD implementations here are provided by Eddy Stryker.
 	// NOTE: there are currently unused in protocols
 	LLSD asLLSD() const;
@@ -243,10 +247,10 @@ class LLFlexibleObjectData final : public LLNetworkData
 
 	//------ the constructor for the structure ------------
 	LLFlexibleObjectData();
-	BOOL pack(LLDataPacker &dp) const;
-	BOOL unpack(LLDataPacker &dp);
-	bool operator==(const LLNetworkData& data) const;
-	void copy(const LLNetworkData& data);
+	BOOL pack(LLDataPacker &dp) const override;
+	BOOL unpack(LLDataPacker &dp) override;
+	bool operator==(const LLNetworkData& data) const override;
+	void copy(const LLNetworkData& data) override;
 	LLSD asLLSD() const;
 	operator LLSD() const { return asLLSD(); }
 	bool fromLLSD(LLSD& sd);
@@ -262,10 +266,10 @@ class LLSculptParams final : public LLNetworkData
 	
 public:
 	LLSculptParams();
-	/*virtual*/ BOOL pack(LLDataPacker &dp) const;
-	/*virtual*/ BOOL unpack(LLDataPacker &dp);
-	/*virtual*/ bool operator==(const LLNetworkData& data) const;
-	/*virtual*/ void copy(const LLNetworkData& data);
+	/*virtual*/ BOOL pack(LLDataPacker &dp) const override;
+	/*virtual*/ BOOL unpack(LLDataPacker &dp) override;
+	/*virtual*/ bool operator==(const LLNetworkData& data) const override;
+	/*virtual*/ void copy(const LLNetworkData& data) override;
 	LLSD asLLSD() const;
 	operator LLSD() const { return asLLSD(); }
 	bool fromLLSD(LLSD& sd);
@@ -283,10 +287,10 @@ class LLLightImageParams final : public LLNetworkData
 	
 public:
 	LLLightImageParams();
-	/*virtual*/ BOOL pack(LLDataPacker &dp) const;
-	/*virtual*/ BOOL unpack(LLDataPacker &dp);
-	/*virtual*/ bool operator==(const LLNetworkData& data) const;
-	/*virtual*/ void copy(const LLNetworkData& data);
+	/*virtual*/ BOOL pack(LLDataPacker &dp) const override;
+	/*virtual*/ BOOL unpack(LLDataPacker &dp) override;
+	/*virtual*/ bool operator==(const LLNetworkData& data) const override;
+	/*virtual*/ void copy(const LLNetworkData& data) override;
 	LLSD asLLSD() const;
 	operator LLSD() const { return asLLSD(); }
 	bool fromLLSD(LLSD& sd);
diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp
index 0932cc120be..b65f3ae7089 100644
--- a/indra/newview/llmanipscale.cpp
+++ b/indra/newview/llmanipscale.cpp
@@ -51,6 +51,7 @@
 #include "llstatusbar.h"
 #include "llui.h"
 #include "llviewercamera.h"
+#include "llviewernetwork.h"
 #include "llviewerobject.h"
 #include "llviewerregion.h"
 #include "llviewerwindow.h"
@@ -94,11 +95,11 @@ F32 get_default_max_prim_scale(bool is_flora)
 	if (gMeshRepo.meshRezEnabled() &&
 		!is_flora)
 	{
-		return DEFAULT_MAX_PRIM_SCALE;
+		return LLWorld::getInstance()->getRegionMaxPrimScale();
 	}
 	else
 	{
-		return DEFAULT_MAX_PRIM_SCALE_NO_MESH;
+		return LLWorld::getInstance()->getRegionMaxPrimScaleNoMesh();
 	}
 }
 
@@ -945,9 +946,10 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 		}
 	}
 
+	LLWorld* world_inst = LLWorld::getInstanceFast();
 
-	F32 max_scale_factor = get_default_max_prim_scale() / MIN_PRIM_SCALE;
-	F32 min_scale_factor = MIN_PRIM_SCALE / get_default_max_prim_scale();
+	F32 max_scale_factor = LLWorld::getInstance()->getRegionMaxPrimScale() / LLWorld::getInstance()->getRegionMinPrimScale();
+	F32 min_scale_factor = LLWorld::getInstance()->getRegionMinPrimScale() / LLWorld::getInstance()->getRegionMaxPrimScale();
 
 	// find max and min scale factors that will make biggest object hit max absolute scale and smallest object hit min absolute scale
 	for (LLObjectSelection::iterator iter = mObjectSelection->begin();
@@ -962,10 +964,16 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 		{
 			const LLVector3& scale = selectNode->mSavedScale;
 
-			F32 cur_max_scale_factor = llmin( get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VX], get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VY], get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VZ] );
+			F32 cur_max_scale_factor = llmin( get_default_max_prim_scale(LLPickInfo::isFlora(cur)) 
+											 / scale.mV[VX], get_default_max_prim_scale(LLPickInfo::isFlora(cur))
+											 / scale.mV[VY], get_default_max_prim_scale(LLPickInfo::isFlora(cur))
+											 / scale.mV[VZ] );
 			max_scale_factor = llmin( max_scale_factor, cur_max_scale_factor );
 
-			F32 cur_min_scale_factor = llmax( MIN_PRIM_SCALE / scale.mV[VX], MIN_PRIM_SCALE / scale.mV[VY], MIN_PRIM_SCALE / scale.mV[VZ] );
+			F32 cur_min_scale_factor = llmax( LLWorld::getInstance()->getRegionMinPrimScale()
+											 / scale.mV[VX], LLWorld::getInstance()->getRegionMinPrimScale()
+											 / scale.mV[VY], LLWorld::getInstance()->getRegionMinPrimScale()
+											 / scale.mV[VZ] );
 			min_scale_factor = llmax( min_scale_factor, cur_min_scale_factor );
 		}
 	}
@@ -975,7 +983,6 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 	LLVector3d drag_global = uniform ? mDragStartCenterGlobal : mDragFarHitGlobal;
 
 	// do the root objects i.e. (TRUE == cur->isRootEdit())
-	LLWorld* world_inst = LLWorld::getInstanceFast();
 	for (LLObjectSelection::iterator iter = mObjectSelection->begin();
 		 iter != mObjectSelection->end(); iter++)
 	{
@@ -1005,11 +1012,8 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 			{
 				// counter-translate child objects if we are moving the root as an individual
 				LLViewerObject::const_child_list_t& child_list = cur->getChildren();
-				for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin();
-					 iter != child_list.end(); iter++)
+				for (LLViewerObject* childp : child_list)
 				{
-					LLViewerObject* childp = *iter;
-
 					if (cur->isAttachment())
 					{
 						LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit());
@@ -1259,7 +1263,7 @@ void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVecto
 
 			F32 denom = axis * dir_local;
 			F32 desired_delta_size	= is_approx_zero(denom) ? 0.f : (delta_local_mag / denom);  // in meters
-			F32 desired_scale		= llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_PRIM_SCALE, get_default_max_prim_scale(LLPickInfo::isFlora(cur)));
+			F32 desired_scale		= llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, LLWorld::getInstance()->getRegionMinPrimScale(), get_default_max_prim_scale(LLPickInfo::isFlora(cur)));
 			// propagate scale constraint back to position offset
 			desired_delta_size		= desired_scale - selectNode->mSavedScale.mV[axis_index]; // propagate constraint back to position
 
@@ -1301,10 +1305,8 @@ void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVecto
 			{
 				// counter-translate child objects if we are moving the root as an individual
 				LLViewerObject::const_child_list_t& child_list = cur->getChildren();
-				for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin();
-					 iter != child_list.end(); iter++)
+				for (LLViewerObject* childp : child_list)
 				{
-					LLViewerObject* childp = *iter;
 					if (!getUniform())
 					{
 						LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit());
@@ -1331,7 +1333,7 @@ void LLManipScale::renderGuidelinesPart( const LLBBox& bbox )
 
 	guideline_end -= guideline_start;
 	guideline_end.normalize();
-	guideline_end *= LLWorld::getInstanceFast()->getRegionWidthInMeters();
+	guideline_end *=  gAgent.getRegion() ? gAgent.getRegion()->getWidth() : LLWorld::getInstanceFast()->getRegionWidthInMeters();
 	guideline_end += guideline_start;
 
 	{
@@ -2047,7 +2049,7 @@ F32		LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const
 			min_extent = bbox_extents.mV[i];
 		}
 	}
-	F32 min_scale_factor = bbox_extents.length() * MIN_PRIM_SCALE / min_extent;
+	F32 min_scale_factor = bbox_extents.magVec() * LLWorld::getInstance()->getRegionMinPrimScale() / min_extent;
 
 	if (getUniform())
 	{
diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp
index 7e5b4b62f67..f0f4e01748d 100644
--- a/indra/newview/llmaniptranslate.cpp
+++ b/indra/newview/llmaniptranslate.cpp
@@ -736,9 +736,9 @@ BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
 				}
 
 				// For safety, cap heights where objects can be dragged
-				if (new_position_global.mdV[VZ] > MAX_OBJECT_Z)
+				if (new_position_global.mdV[VZ] > LLWorld::getInstanceFast()->getRegionMaxHeight())
 				{
-					new_position_global.mdV[VZ] = MAX_OBJECT_Z;
+					new_position_global.mdV[VZ] = LLWorld::getInstanceFast()->getRegionMaxHeight();
 				}
 
 				// Grass is always drawn on the ground, so clamp its position to the ground
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index b4e09c50002..0427df21b58 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -57,6 +57,7 @@
 #include "llviewertexteditor.h"
 #include "llviewertexturelist.h"
 #include "llvoavatar.h"
+#include "llworld.h"
 #include "pipeline.h"
 
 // ui controls (from floater)
@@ -662,7 +663,8 @@ void LLModelPreview::rebuildUploadData()
         setLoadState(LLModelLoader::DONE);
     }
 
-    F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE - 0.1f) / max_scale;
+    F32 region_max_prim_scale = LLWorld::getInstance()->getRegionMaxPrimScale();
+    F32 max_import_scale = region_max_prim_scale/max_scale;
 
     F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]);
     max_axis = llmax(max_axis, mPreviewScale.mV[2]);
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
index 039e7f4a6cd..d4ce93739c1 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -63,6 +63,7 @@
 #include "llworld.h"
 #include "pipeline.h"
 #include "llviewercontrol.h"
+#include "llviewernetwork.h"
 #include "lluictrlfactory.h"
 //#include "llfirstuse.h"
 // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a)
@@ -303,7 +304,9 @@ LLPanelObject::LLPanelObject()
 	mSelectedType(MI_BOX),
 	mSculptTextureRevert(LLUUID::null),
 	mSculptTypeRevert(0),
-	mSizeChanged(FALSE)
+	mSizeChanged(FALSE),
+	mRegionMaxHeight(256.f),
+	mRegionMaxDepth(0.f)
 {
 }
 
@@ -417,8 +420,8 @@ void LLPanelObject::getState( )
 	mCtrlPosX->setMaxValue(is_attachment ? MAX_ATTACHMENT_DIST : width);
 	mCtrlPosY->setMinValue(is_attachment ? -MAX_ATTACHMENT_DIST : -width);
 	mCtrlPosY->setMaxValue(is_attachment ? MAX_ATTACHMENT_DIST : width);
-	mCtrlPosZ->setMinValue(is_attachment ? -MAX_ATTACHMENT_DIST : 0);
-	mCtrlPosZ->setMaxValue(is_attachment ? MAX_ATTACHMENT_DIST : 4096);
+	mCtrlPosZ->setMinValue(is_attachment ? -MAX_ATTACHMENT_DIST : mRegionMaxDepth);
+	mCtrlPosZ->setMaxValue(is_attachment ? MAX_ATTACHMENT_DIST : mRegionMaxHeight);
 
 	if (enable_scale)
 	{
@@ -993,9 +996,9 @@ void LLPanelObject::getState( )
 		mSpinScaleY->set( scale_y );
 		calcp->setVar(LLCalc::X_HOLE, scale_x);
 		calcp->setVar(LLCalc::Y_HOLE, scale_y);
-		mSpinScaleX->setMinValue(OBJECT_MIN_HOLE_SIZE);
+		mSpinScaleX->setMinValue(mMinHoleSize);
 		mSpinScaleX->setMaxValue(OBJECT_MAX_HOLE_SIZE_X);
-		mSpinScaleY->setMinValue(OBJECT_MIN_HOLE_SIZE);
+		mSpinScaleY->setMinValue(mMinHoleSize);
 		mSpinScaleY->setMaxValue(OBJECT_MAX_HOLE_SIZE_Y);
 		break;
 	default:
@@ -2000,12 +2003,44 @@ void LLPanelObject::refresh()
 	{
 		mRootObject = NULL;
 	}
-	
-	F32 max_scale = get_default_max_prim_scale(LLPickInfo::isFlora(mObject));
 
-	mCtrlScaleX->setMaxValue(max_scale);
-	mCtrlScaleY->setMaxValue(max_scale);
-	mCtrlScaleZ->setMaxValue(max_scale);
+	LLWorld* worldp = LLWorld::getInstanceFast();
+	if (mObject && mObject->getRegion())
+	{
+		auto region = mObject->getRegion();
+
+		mRegionMaxHeight = region->getMaxRegionHeight();
+		mRegionMaxDepth = region->getMinRegionHeight();
+		mCtrlPosZ->setMaxValue(mRegionMaxHeight);
+		mMinScale = region->getMinPrimScale();
+		mMaxScale = LLGridManager::getInstanceFast()->isInOpenSimulator() ? region->getMaxPrimScale() : get_default_max_prim_scale(LLPickInfo::isFlora(mObject));
+		mCtrlScaleX->setMinValue(mMinScale);
+		mCtrlScaleX->setMaxValue(mMaxScale);
+		mCtrlScaleY->setMinValue(mMinScale);
+		mCtrlScaleY->setMaxValue(mMaxScale);
+		mCtrlScaleZ->setMinValue(mMinScale);
+		mCtrlScaleZ->setMaxValue(mMaxScale);
+	}
+	else
+	{
+		mRegionMaxHeight = worldp->getRegionMaxHeight();
+		mRegionMaxDepth = LLGridManager::getInstanceFast()->isInOpenSimulator() ? -256.f : 0.f; // OpenSim is derp
+		mCtrlPosZ->setMaxValue(mRegionMaxHeight);
+		mMinScale = worldp->getRegionMinPrimScale();
+		mMaxScale = get_default_max_prim_scale(LLPickInfo::isFlora(mObject));
+		mCtrlScaleX->setMinValue(mMinScale);
+		mCtrlScaleX->setMaxValue(mMaxScale);
+		mCtrlScaleY->setMinValue(mMinScale);
+		mCtrlScaleY->setMaxValue(mMaxScale);
+		mCtrlScaleZ->setMinValue(mMinScale);
+		mCtrlScaleZ->setMaxValue(mMaxScale);
+	}
+
+	mMaxHollowSize = worldp->getRegionMaxHollowSize();
+	mSpinHollow->setMaxValue(mMaxHollowSize);
+	mMinHoleSize = worldp->getRegionMinHoleSize();
+	mSpinScaleX->setMinValue(mMinHoleSize);
+	mSpinScaleY->setMinValue(mMinHoleSize);
 }
 
 
diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h
index 8829f493fa7..b5f9d129464 100644
--- a/indra/newview/llpanelobject.h
+++ b/indra/newview/llpanelobject.h
@@ -44,17 +44,17 @@ class LLTextureCtrl;
 class LLInventoryItem;
 class LLUUID;
 
-class LLPanelObject : public LLPanel
+class LLPanelObject final : public LLPanel
 {
 public:
 	LLPanelObject();
 	virtual ~LLPanelObject();
 
-	virtual BOOL	postBuild();
-	virtual void	draw();
-	virtual void 	clearCtrls();
+	BOOL	postBuild() override;
+	void	draw() override;
+	void 	clearCtrls() override;
 
-	void			refresh();
+	void			refresh() override;
 
 	static bool		precommitValidate(const LLSD& data);
 	
@@ -166,6 +166,13 @@ class LLPanelObject : public LLPanel
 
 	LLUUID          mSculptTextureRevert;   // so we can revert the sculpt texture on cancel
 	U8              mSculptTypeRevert;      // so we can revert the sculpt type on cancel
+	
+	F32				mRegionMaxHeight;
+	F32				mRegionMaxDepth;
+	F32				mMinScale;
+	F32				mMaxScale;
+	F32				mMaxHollowSize;
+	F32				mMinHoleSize;
 
 	LLPointer<LLViewerObject> mObject;
 	LLPointer<LLViewerObject> mRootObject;
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 5df6033cc94..f7e4fe599a4 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -91,6 +91,7 @@
 #include "llviewerstats.h"
 #include "llvoavatarself.h"
 #include "llvovolume.h"
+#include "llworld.h"
 #include "pipeline.h"
 #include "llviewershadermgr.h"
 #include "llpanelface.h"
@@ -632,11 +633,12 @@ bool LLSelectMgr::linkObjects()
 	}
 
 	S32 object_count = getSelection()->getObjectCount();
-	if (object_count > MAX_CHILDREN_PER_TASK + 1)
+	S32 max_objects = LLWorld::getInstance()->getRegionMaxLinkObjects();
+	if (object_count > max_objects + 1)
 	{
 		LLSD args;
 		args["COUNT"] = llformat("%d", object_count);
-		int max = MAX_CHILDREN_PER_TASK+1;
+		int max = max_objects + 1;
 		args["MAX"] = llformat("%d", max);
 		LLNotificationsUtil::add("UnableToLinkObjects", args);
 		return true;
diff --git a/indra/newview/lltoolgrab.cpp b/indra/newview/lltoolgrab.cpp
index 5d281c4c925..dfbcb21ff41 100644
--- a/indra/newview/lltoolgrab.cpp
+++ b/indra/newview/lltoolgrab.cpp
@@ -660,9 +660,9 @@ void LLToolGrabBase::handleHoverActive(S32 x, S32 y, MASK mask)
 			}
 
 			// For safety, cap heights where objects can be dragged
-			if (grab_point_global.mdV[VZ] > MAX_OBJECT_Z)
+			if (grab_point_global.mdV[VZ] > LLWorld::getInstanceFast()->getRegionMaxHeight())
 			{
-				grab_point_global.mdV[VZ] = MAX_OBJECT_Z;
+				grab_point_global.mdV[VZ] = LLWorld::getInstanceFast()->getRegionMaxHeight();
 			}
 
 			grab_point_global = LLWorld::getInstanceFast()->clipToVisibleRegions(mDragStartPointGlobal, grab_point_global);
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index ae5e57c3284..08f63bdc631 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -2317,14 +2317,44 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)
 		setGodnames();
 		if (mSimulatorFeatures.has("OpenSimExtras"))
 		{
-			if (mSimulatorFeatures["OpenSimExtras"].has("GridURL"))
+			const LLSD& extras(mSimulatorFeatures["OpenSimExtras"]);
+
+			if (extras.has("GridURL"))
 			{
-				const std::string& grid_url = mSimulatorFeatures["OpenSimExtras"]["GridURL"].asString();
+				const std::string& grid_url = extras["GridURL"].asString();
 				if (LLGridManager::getInstance()->getGrid(LLURI(grid_url).authority()).empty())
 					LLGridManager::getInstance()->addRemoteGrid(grid_url, LLGridManager::ADD_HYPERGRID);
 			}
+
+			mMinSimHeight = extras.has("MinSimHeight") ? extras["MinSimHeight"].asReal() : OS_MIN_OBJECT_Z;
+			mMaxSimHeight = extras.has("MaxSimHeight") ? extras["MaxSimHeight"].asReal() : OS_MAX_OBJECT_Z;
+			mMinPrimScale = extras.has("MinPrimScale") ? extras["MinPrimScale"].asReal() : OS_MIN_PRIM_SCALE;
+			mMaxPrimScale = extras.has("MaxPrimScale") ? extras["MaxPrimScale"].asReal() : OS_DEFAULT_MAX_PRIM_SCALE;
+			mMaxPrimScaleNoMesh = extras.has("MaxPrimScale") ? extras["MaxPrimScale"].asReal() : OS_DEFAULT_MAX_PRIM_SCALE;
+			mMinPhysPrimScale = extras.has("MinPhysPrimScale") ? extras["MinPhysPrimScale"].asReal() : OS_MIN_PRIM_SCALE;
+			mMaxPhysPrimScale = extras.has("MaxPhysPrimScale") ? extras["MaxPhysPrimScale"].asReal() : OS_DEFAULT_MAX_PRIM_SCALE;
+		}
+		else
+		{
+			mMinSimHeight = OS_MIN_OBJECT_Z;
+			mMaxSimHeight = OS_MAX_OBJECT_Z;
+			mMinPrimScale = OS_MIN_PRIM_SCALE;
+			mMaxPrimScale = OS_DEFAULT_MAX_PRIM_SCALE;
+			mMaxPrimScaleNoMesh = OS_DEFAULT_MAX_PRIM_SCALE;
+			mMinPhysPrimScale = OS_MIN_PRIM_SCALE;
+			mMaxPhysPrimScale = OS_DEFAULT_MAX_PRIM_SCALE;
 		}
 	}
+	else
+	{
+		mMinSimHeight = SL_MIN_OBJECT_Z;
+		mMaxSimHeight = SL_MAX_OBJECT_Z;
+		mMinPrimScale = SL_MIN_PRIM_SCALE;
+		mMaxPrimScale = SL_DEFAULT_MAX_PRIM_SCALE;
+		mMaxPrimScaleNoMesh = SL_DEFAULT_MAX_PRIM_SCALE_NO_MESH;
+		mMinPhysPrimScale = SL_MIN_PRIM_SCALE;
+		mMaxPhysPrimScale = SL_DEFAULT_MAX_PRIM_SCALE;
+	}
 
 	if(mSimulatorFeatures.has("MaxMaterialsPerTransaction")
 		&& mSimulatorFeatures["MaxMaterialsPerTransaction"].isInteger())
@@ -3698,6 +3728,36 @@ U32 LLViewerRegion::getWhisperRange() const
     return range;
 }
 
+F32 LLViewerRegion::getMinPrimScale() const
+{
+	return mMinPrimScale;
+}
+
+F32 LLViewerRegion::getMaxPrimScale() const
+{
+	return mMaxPrimScale;
+}
+
+F32 LLViewerRegion::getMinPhysPrimScale() const
+{
+	return mMinPhysPrimScale;
+}
+
+F32 LLViewerRegion::getMaxPhysPrimScale() const
+{
+	return mMaxPhysPrimScale;
+}
+
+F32 LLViewerRegion::getMinRegionHeight() const
+{
+	return mMinSimHeight;
+}
+
+F32 LLViewerRegion::getMaxRegionHeight() const
+{
+	return mMaxSimHeight;
+}
+
 void LLViewerRegion::setGodnames()
 {
 	mGodNames.clear();
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index 73369d6065c..27f2e19c672 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -430,6 +430,14 @@ class LLViewerRegion final : public LLCapabilityProvider // implements this inte
     U32 getShoutRange() const;
     /// Whisper Range (0.8.1)
     U32 getWhisperRange() const;
+	/// Prim Scale
+	F32 getMinPrimScale() const;
+	F32 getMaxPrimScale() const;
+	F32 getMinPhysPrimScale() const;
+	F32 getMaxPhysPrimScale() const;
+	/// Sim Z
+	F32 getMinRegionHeight() const;
+	F32 getMaxRegionHeight() const;
 
 	/// "God names" surname and full account names map
 	const auto& getGods() const { return mGodNames; };
@@ -608,6 +616,13 @@ class LLViewerRegion final : public LLCapabilityProvider // implements this inte
 	bool mMeshRezEnabled = false;
 	bool mDynamicPathfindingEnabled = false;
 	bool mAvatarHoverHeightEnabled = false;
+	F32  mMinSimHeight = SL_MIN_OBJECT_Z;
+	F32  mMaxSimHeight = SL_MAX_OBJECT_Z;
+	F32  mMinPrimScale = SL_MIN_PRIM_SCALE;
+	F32  mMaxPrimScale = SL_DEFAULT_MAX_PRIM_SCALE;
+	F32  mMaxPrimScaleNoMesh = SL_DEFAULT_MAX_PRIM_SCALE_NO_MESH;
+	F32  mMinPhysPrimScale = SL_MIN_PRIM_SCALE;
+	F32  mMaxPhysPrimScale = SL_DEFAULT_MAX_PRIM_SCALE;
 
     typedef std::map<U32, LLPointer<LLVOCacheEntry> >	   vocache_entry_map_t;
     static vocache_entry_map_t sRegionCacheCleanup;
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index 3b09ea1845f..89c02517cfb 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -72,6 +72,9 @@ U32			gAgentPauseSerialNum = 0;
 //
 const S32 WORLD_PATCH_SIZE = 16;
 
+const S32 SL_MAX_LINK_OBJECTS = 255;
+const S32 OS_MAX_LINK_OBJECTS = 8191;	// Magic and arbitrary
+
 extern LLColor4U MAX_WATER_COLOR;
 
 U32 LLWorld::mWidth = 256;
@@ -88,6 +91,7 @@ F32 LLWorld::mWidthInMeters = mWidth * mScale;
 
 // allocate the stack
 LLWorld::LLWorld() :
+	mRefreshLimits(true),
 	mLandFarClip(DEFAULT_FAR_PLANE),
 	mLastPacketsIn(0),
 	mLastPacketsOut(0),
@@ -277,6 +281,8 @@ LLViewerRegion* LLWorld::addRegion(const U64 &region_handle, const LLHost &host)
 	}
 
 	updateWaterObjects();
+	if (mRefreshLimits)
+		refreshLimits();
 
 	return regionp;
 }
@@ -785,6 +791,32 @@ void LLWorld::clearAllVisibleObjects()
 	}
 }
 
+void LLWorld::refreshLimits()
+{
+	mRefreshLimits = false;
+
+	if (LLGridManager::getInstance()->isInOpenSim())
+	{
+		mRegionMaxHeight = OS_MAX_OBJECT_Z; //llmath/xform.h
+		mRegionMinPrimScale = OS_MIN_PRIM_SCALE;
+		mRegionMaxPrimScale = OS_DEFAULT_MAX_PRIM_SCALE;
+		mRegionMaxPrimScaleNoMesh = OS_DEFAULT_MAX_PRIM_SCALE; // no restrictions here
+		mRegionMaxHollowSize = OS_OBJECT_MAX_HOLLOW_SIZE;
+		mRegionMinHoleSize = OS_OBJECT_MIN_HOLE_SIZE;
+		mRegionMaxLinkObjects = OS_MAX_LINK_OBJECTS;
+	}
+	else
+	{
+		mRegionMaxHeight = SL_MAX_OBJECT_Z;
+		mRegionMinPrimScale = SL_MIN_PRIM_SCALE;
+		mRegionMaxPrimScale = SL_DEFAULT_MAX_PRIM_SCALE;
+		mRegionMaxPrimScaleNoMesh = SL_DEFAULT_MAX_PRIM_SCALE_NO_MESH;
+		mRegionMaxHollowSize = SL_OBJECT_MAX_HOLLOW_SIZE;
+		mRegionMinHoleSize = SL_OBJECT_MIN_HOLE_SIZE;
+		mRegionMaxLinkObjects = SL_MAX_LINK_OBJECTS;
+	}
+}
+
 void LLWorld::updateParticles()
 {
 	LLViewerPartSim::getInstanceFast()->updateSimulation();
diff --git a/indra/newview/llworld.h b/indra/newview/llworld.h
index d79706a8561..78f6c3bd208 100644
--- a/indra/newview/llworld.h
+++ b/indra/newview/llworld.h
@@ -115,7 +115,13 @@ class LLWorld final : public LLSingleton<LLWorld>
 	// region X and Y size in meters
 	F32						getRegionWidthInMeters() const	{ return mWidthInMeters; }
 	F32						getRegionMinHeight() const		{ return -mWidthInMeters; }
-	F32						getRegionMaxHeight() const		{ return MAX_OBJECT_Z; }
+	F32						getRegionMaxHeight() const		{ return mRegionMaxHeight; }
+	F32						getRegionMinPrimScale() const	{ return mRegionMinPrimScale; }
+	F32						getRegionMaxPrimScale() const	{ return mRegionMaxPrimScale; }
+	F32						getRegionMaxPrimScaleNoMesh() const	{ return mRegionMaxPrimScaleNoMesh; }
+	F32						getRegionMaxHollowSize() const	{ return mRegionMaxHollowSize; }
+	F32						getRegionMinHoleSize() const	{ return mRegionMinHoleSize; }
+	S32						getRegionMaxLinkObjects() const	{ return mRegionMaxLinkObjects; }
 
 	void					updateRegions(F32 max_update_time);
 	void					updateVisibilities();
@@ -152,6 +158,8 @@ class LLWorld final : public LLSingleton<LLWorld>
 	U32  getNumOfActiveCachedObjects() const {return mNumOfActiveCachedObjects;}
 
 	void clearAllVisibleObjects();
+	void refreshLimits();
+
 public:
 	typedef std::list<LLViewerRegion*> region_list_t;
 	const region_list_t& getRegionList() const { return mActiveRegionList; }
@@ -204,6 +212,14 @@ class LLWorld final : public LLSingleton<LLWorld>
 	static const F32 mScale;
 
 	static F32 mWidthInMeters;
+	F32 mRegionMaxHeight;
+	F32 mRegionMinPrimScale;
+	F32 mRegionMaxPrimScale;
+	F32 mRegionMaxPrimScaleNoMesh;
+	F32 mRegionMaxHollowSize;
+	F32 mRegionMinHoleSize;
+	S32 mRegionMaxLinkObjects;
+	bool mRefreshLimits;
 
 	F32 mLandFarClip;					// Far clip distance for land.
 	LLPatchVertexArray		mLandPatch;
-- 
GitLab