Fix the sending of large scale terrain transactions, such as 'terrain fill', to clients. It was sending all of the terrain that was edited, regardless of whether they were able to see the change happen.

--- OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
+++ OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
@@ -27,9 +27,9 @@

 using System;
 using System.Collections.Generic;
-using System.IO;
-using System.Reflection;
-using System.Net;
+using System.IO;
+using System.Reflection;
+using System.Net;
 using System.Timers;
 using log4net;
 using Nini.Config;
@@ -60,7 +60,7 @@
             Noise = 4,
             Revert = 5,

-            // Extended brushes
+            // Extended brushes for Aurora
             Erode = 255,
             Weather = 254,
             Olsen = 253
@@ -79,24 +79,24 @@
         private readonly Dictionary<string, ITerrainLoader> m_loaders = new Dictionary<string, ITerrainLoader>();

         private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects =
-            new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>();
-
-        private ITerrainChannel m_channel;
-        private ITerrainChannel m_waterChannel;
-        private Dictionary<string, ITerrainEffect> m_plugineffects;
-        private ITerrainChannel m_revert;
-        private ITerrainChannel m_waterRevert;
-        private Scene m_scene;
-
-        private long m_queueNextSave = 0;
-        private int m_savetime = 2; // seconds to wait before saving terrain
+            new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>();
+
+        private ITerrainChannel m_channel;
+        private ITerrainChannel m_waterChannel;
+        private Dictionary<string, ITerrainEffect> m_plugineffects;
+        private ITerrainChannel m_revert;
+        private ITerrainChannel m_waterRevert;
+        private Scene m_scene;
+
+        private long m_queueNextSave = 0;
+        private int m_savetime = 2; // seconds to wait before saving terrain
         private Timer m_queueTimer = new Timer();

         private const double MAX_HEIGHT = 250;
         private const double MIN_HEIGHT = -100;
         private readonly UndoStack<LandUndoState> m_undo = new UndoStack<LandUndoState>(5);
-        private bool m_sendTerrainUpdatesByViewDistance = false;
-        protected Dictionary<UUID, bool[,]> m_terrainPatchesSent = new Dictionary<UUID, bool[,]>();
+        private bool m_sendTerrainUpdatesByViewDistance = false;
+        protected Dictionary<UUID, bool[,]> m_terrainPatchesSent = new Dictionary<UUID, bool[,]>();
         protected bool m_use3DWater = false;

         #region INonSharedRegionModule Members
@@ -107,12 +107,12 @@
         /// <param name="scene">Region initialising</param>
         /// <param name="config">Config for the region</param>
         public void Initialise(IConfigSource config)
-        {
-            if (config.Configs["TerrainModule"] != null)
-            {
-                m_sendTerrainUpdatesByViewDistance = config.Configs["TerrainModule"].GetBoolean("SendTerrainByViewDistance", m_sendTerrainUpdatesByViewDistance);
-                m_use3DWater = config.Configs["TerrainModule"].GetBoolean("Use3DWater", m_use3DWater);
-                m_savetime = config.Configs["TerrainModule"].GetInt("QueueSaveTime", m_savetime);
+        {
+            if (config.Configs["TerrainModule"] != null)
+            {
+                m_sendTerrainUpdatesByViewDistance = config.Configs["TerrainModule"].GetBoolean("SendTerrainByViewDistance", m_sendTerrainUpdatesByViewDistance);
+                m_use3DWater = config.Configs["TerrainModule"].GetBoolean("Use3DWater", m_use3DWater);
+                m_savetime = config.Configs["TerrainModule"].GetInt("QueueSaveTime", m_savetime);
             }
         }

@@ -121,29 +121,29 @@
             bool firstScene = m_scenes.Count == 0;
             m_scene = scene;
             m_scenes.Add(scene);
-            m_terrainModules.Add(this);
-
-            LoadWorldHeightmap();
-            LoadWorldWaterMap();
-            scene.SceneGraph.PhysicsScene.SetTerrain(m_channel.GetFloatsSerialised(scene), m_channel.GetDoubles(scene));
+            m_terrainModules.Add(this);
+
+            LoadWorldHeightmap();
+            LoadWorldWaterMap();
+            scene.SceneGraph.PhysicsScene.SetTerrain(m_channel.GetFloatsSerialised(scene), m_channel.GetDoubles(scene));
             UpdateWaterHeight(scene.RegionInfo.RegionSettings.WaterHeight);

             m_scene.RegisterModuleInterface<ITerrainModule>(this);
             m_scene.EventManager.OnNewClient += EventManager_OnNewClient;
-            m_scene.EventManager.OnClosingClient += OnClosingClient;
-            m_scene.EventManager.OnSignificantClientMovement += EventManager_OnSignificantClientMovement;
-            m_scene.AuroraEventManager.OnGenericEvent += AuroraEventManager_OnGenericEvent;
-            m_scene.EventManager.OnNewPresence += OnNewPresence;
-
+            m_scene.EventManager.OnClosingClient += OnClosingClient;
+            m_scene.EventManager.OnSignificantClientMovement += EventManager_OnSignificantClientMovement;
+            m_scene.AuroraEventManager.OnGenericEvent += AuroraEventManager_OnGenericEvent;
+            m_scene.EventManager.OnNewPresence += OnNewPresence;
+
             if (firstScene)
                 AddConsoleCommands();

-            InstallDefaultEffects();
-            LoadPlugins();
-
-            m_queueTimer.Enabled = false;
-            m_queueTimer.AutoReset = true;
-            m_queueTimer.Interval = m_savetime * 1000;
+            InstallDefaultEffects();
+            LoadPlugins();
+
+            m_queueTimer.Enabled = false;
+            m_queueTimer.AutoReset = true;
+            m_queueTimer.Interval = m_savetime * 1000;
             m_queueTimer.Elapsed += TerrainUpdateTimer;
         }

@@ -155,16 +155,16 @@
         {
             lock (m_scene)
             {
-                // remove the event-handlers
-                m_scene.EventManager.OnNewClient -= EventManager_OnNewClient;
-                m_scene.EventManager.OnClosingClient -= OnClosingClient;
-                m_scene.EventManager.OnSignificantClientMovement -= EventManager_OnSignificantClientMovement;
-                m_scene.AuroraEventManager.OnGenericEvent -= AuroraEventManager_OnGenericEvent;
+                // remove the event-handlers
+                m_scene.EventManager.OnNewClient -= EventManager_OnNewClient;
+                m_scene.EventManager.OnClosingClient -= OnClosingClient;
+                m_scene.EventManager.OnSignificantClientMovement -= EventManager_OnSignificantClientMovement;
+                m_scene.AuroraEventManager.OnGenericEvent -= AuroraEventManager_OnGenericEvent;
                 m_scene.EventManager.OnNewPresence -= OnNewPresence;

                 // remove the interface
                 m_scene.UnregisterModuleInterface<ITerrainModule>(this);
-            }
+            }
         }

         public void Close()
@@ -181,485 +181,485 @@
             get { return "TerrainModule"; }
         }

-        #endregion
-
-        #region ITerrainModule Members
-
-        public void TerrainUpdateTimer(object sender, EventArgs ea)
-        {
-            long now = DateTime.Now.Ticks;
-
-            if (m_queueNextSave > 0 && m_queueNextSave < now)
-            {
-                //Save the terarin
-                SaveTerrain();
-                m_scene.SceneGraph.PhysicsScene.SetTerrain(m_channel.GetFloatsSerialised(m_scene), m_channel.GetDoubles(m_scene));
-
-                m_queueNextSave = 0;
-                m_queueTimer.Stop();
-            }
-        }
-
-        public void QueueTerrainUpdate()
-        {
-            m_queueNextSave = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000);
-            m_queueTimer.Start();
-        }
-
-        public void UpdateWaterHeight(double height)
-        {
-            float[] waterMap;
-            if (m_waterChannel == null)
-            {
-                waterMap = new float[m_scene.RegionInfo.RegionSizeX * m_scene.RegionInfo.RegionSizeY];
-                for (int x = 0; x < m_scene.RegionInfo.RegionSizeX; x++)
-                {
-                    for (int y = 0; y < m_scene.RegionInfo.RegionSizeY; y++)
-                    {
-                        waterMap[y * m_scene.RegionInfo.RegionSizeX + x] = (float)height;
-                    }
-                }
-            }
-            else
-                waterMap = m_waterChannel.GetFloatsSerialised(m_scene);
-            m_scene.SceneGraph.PhysicsScene.SetWaterLevel(waterMap);
-        }
-
-        /// <summary>
-        /// Installs terrain brush hook to IClientAPI
-        /// </summary>
-        /// <param name="client"></param>
-        private void EventManager_OnNewClient(IClientAPI client)
-        {
-            client.OnModifyTerrain += client_OnModifyTerrain;
-            client.OnBakeTerrain += client_OnBakeTerrain;
-            client.OnLandUndo += client_OnLandUndo;
-            client.OnGodlikeMessage += client_onGodlikeMessage;
-            client.OnUnackedTerrain += client_OnUnackedTerrain;
-            client.OnRegionHandShakeReply += SendLayerData;
-
-            //Add them to the cache
-            lock (m_terrainPatchesSent)
-            {
-                if (!m_terrainPatchesSent.ContainsKey(client.AgentId))
-                {
-                    ScenePresence agent = m_scene.GetScenePresence(client.AgentId);
-                    if (agent != null && agent.IsChildAgent)
-                    {
-                        //If the avatar is a child agent, we need to send the terrain data initially
-                        EventManager_OnSignificantClientMovement(client);
-                    }
-                }
-            }
-        }
-
-        private void OnClosingClient(IClientAPI client)
-        {
-            client.OnModifyTerrain -= client_OnModifyTerrain;
-            client.OnBakeTerrain -= client_OnBakeTerrain;
-            client.OnLandUndo -= client_OnLandUndo;
-            client.OnGodlikeMessage -= client_onGodlikeMessage;
-            client.OnUnackedTerrain -= client_OnUnackedTerrain;
-            client.OnRegionHandShakeReply -= SendLayerData;
-
-            //Remove them from the cache
-            lock (m_terrainPatchesSent)
-            {
-                m_terrainPatchesSent.Remove(client.AgentId);
-            }
-        }
-
-        /// <summary>
-        /// Send the region heightmap to the client
-        /// </summary>
-        /// <param name="RemoteClient">Client to send to</param>
-        public void SendLayerData(IClientAPI RemoteClient)
-        {
-            ScenePresence presence = m_scene.GetScenePresence(RemoteClient.AgentId);
-            if (!m_sendTerrainUpdatesByViewDistance)
-            {
-                //Default way, send the full terrain at once
-                RemoteClient.SendLayerData(m_channel.GetFloatsSerialised(RemoteClient.Scene));
-            }
-            else
-            {
-                //Send only what the client can see,
-                //  but the client isn't loaded yet, wait until they get set up
-                //  The first agent update they send will trigger the DrawDistanceChanged event and send the land
-            }
-        }
-
-        void AuroraEventManager_OnGenericEvent(string FunctionName, object parameters)
-        {
-            if (FunctionName == "DrawDistanceChanged" || FunctionName == "SignficantCameraMovement")
-            {
-                SendTerrainUpdatesForClient((ScenePresence)parameters);
-            }
-        }
-
-        void EventManager_OnSignificantClientMovement(IClientAPI remote_client)
-        {
-            ScenePresence presence = m_scene.GetScenePresence(remote_client.AgentId);
-            SendTerrainUpdatesForClient(presence);
-        }
-
-        void OnNewPresence(ScenePresence presence)
-        {
-            SendTerrainUpdatesForClient(presence);
-        }
-
-        protected void SendTerrainUpdatesForClient(ScenePresence presence)
-        {
-            if (!m_sendTerrainUpdatesByViewDistance)
-                return;
-
-            if (presence == null)
-                return;
-
-            if (!m_terrainPatchesSent.ContainsKey(presence.UUID))
-            {
-                int xSize = m_scene.RegionInfo.RegionSizeX != int.MaxValue ? m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize : Constants.RegionSize / Constants.TerrainPatchSize;
-                int ySize = m_scene.RegionInfo.RegionSizeX != int.MaxValue ? m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize : Constants.RegionSize / Constants.TerrainPatchSize;
-                m_terrainPatchesSent.Add(presence.UUID, new bool[xSize,
-                    ySize]);
-            }
-
-            List<int> xs = new List<int>();
-            List<int> ys = new List<int>();
-            if (m_scene.RegionInfo.RegionSizeX != int.MaxValue)
-            {
-                for (int x = 0; x <
-                    m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize; x++) //Make sure that we don't send past what viewers like
-                {
-                    for (int y = 0; y <
-                        m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize; y++) //Make sure that we don't send past what viewers like
-                    {
-                        //Need to make sure we don't send the same ones over and over
-                        if (!m_terrainPatchesSent[presence.UUID][x, y])
-                        {
-                            //Check which has less distance, camera or avatar position, both have to be done
-                            double posdistance = Util.GetFlatDistanceTo(presence.AbsolutePosition,
-                                new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, 0));
-                            double camdistance = Util.GetFlatDistanceTo(presence.CameraPosition,
-                                new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, 0));
-                            //Take the smaller of the two
-                            double distance = posdistance > camdistance ? camdistance : posdistance;
-                            if (distance < presence.DrawDistance + 35) //Its not a radius, its a diameter and we add 35 so that it doesn't look like it cuts off
-                            {
-                                //They can see it, send it ot them
-                                m_terrainPatchesSent[presence.UUID][x, y] = true;
-                                xs.Add(x);
-                                ys.Add(y);
-                                //Wait and send them all at once
-                                //presence.ControllingClient.SendLayerData(x, y, serializedMap);
-                            }
-                        }
-                    }
-                }
-            }
-            else
-            {
-                ///MegaRegion!
-                for (int x = 0; x <
-                    Constants.RegionSize / Constants.TerrainPatchSize; x++)
-                {
-                    for (int y = 0; y <
-                        Constants.RegionSize / Constants.TerrainPatchSize; y++)
-                    {
-                        //Need to make sure we don't send the same ones over and over
-                        if (!m_terrainPatchesSent[presence.UUID][x, y])
-                        {
-                            double distance = Util.GetFlatDistanceTo(presence.AbsolutePosition,
-                                new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, 0));
-                            if (distance < presence.DrawDistance + 35) //Its not a radius, its a diameter and we add 35 so that it doesn't look like it cuts off
-                            {
-                                //They can see it, send it ot them
-                                m_terrainPatchesSent[presence.UUID][x, y] = true;
-                                xs.Add(x);
-                                ys.Add(y);
-                            }
-                        }
-                    }
-                }
-            }
-            float[] serializedTerrainMap = m_channel.GetFloatsSerialised(m_scene);
-            //Send all the terrain patches at once
-            presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), serializedTerrainMap, TerrainPatch.LayerType.Land);
-
-            if (m_use3DWater)
-            {
-                float[] serializedWaterMap = m_waterChannel.GetFloatsSerialised(m_scene);
-                //Send all the water patches at once
-                presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), serializedWaterMap, TerrainPatch.LayerType.Water);
-            }
-        }
-
-        /// <summary>
-        /// Store the terrain in the persistant data store
-        /// </summary>
-        public void SaveTerrain()
-        {
-            m_scene.SimulationDataService.StoreTerrain(m_channel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-        }
-
-        /// <summary>
-        /// Store the terrain in the persistant data store
-        /// </summary>
-        public void SaveWater()
-        {
-            m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-        }
-
-        /// <summary>
-        /// Reset the terrain of this region to the default
-        /// </summary>
-        public void ResetTerrain()
-        {
-            TerrainChannel channel = new TerrainChannel(m_scene);
-            m_channel = channel;
-            SaveRevertTerrain(channel);
-            m_scene.RegisterModuleInterface<ITerrainChannel>(m_channel);
-            CheckForTerrainUpdates(false, true, false);
-        }
-
-        /// <summary>
-        /// Reset the terrain of this region to the default
-        /// </summary>
-        public void ResetWater()
-        {
-            TerrainChannel channel = new TerrainChannel(m_scene);
-            m_waterChannel = channel;
-            SaveRevertWater(m_waterChannel);
-            CheckForTerrainUpdates(false, true, true);
-        }
-
-        /// <summary>
-        /// Store the revert terrain in the persistant data store
-        /// </summary>
-        public void SaveRevertTerrain(ITerrainChannel channel)
-        {
-            m_scene.SimulationDataService.StoreTerrain(m_revert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
-        }
-
-        /// <summary>
-        /// Store the revert terrain in the persistant data store
-        /// </summary>
-        public void SaveRevertWater(ITerrainChannel channel)
-        {
-            m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+        #endregion
+
+        #region ITerrainModule Members
+
+        public void TerrainUpdateTimer(object sender, EventArgs ea)
+        {
+            long now = DateTime.Now.Ticks;
+
+            if (m_queueNextSave > 0 && m_queueNextSave < now)
+            {
+                //Save the terarin
+                SaveTerrain();
+                m_scene.SceneGraph.PhysicsScene.SetTerrain(m_channel.GetFloatsSerialised(m_scene), m_channel.GetDoubles(m_scene));
+
+                m_queueNextSave = 0;
+                m_queueTimer.Stop();
+            }
+        }
+
+        public void QueueTerrainUpdate()
+        {
+            m_queueNextSave = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000);
+            m_queueTimer.Start();
+        }
+
+        public void UpdateWaterHeight(double height)
+        {
+            float[] waterMap;
+            if (m_waterChannel == null)
+            {
+                waterMap = new float[m_scene.RegionInfo.RegionSizeX * m_scene.RegionInfo.RegionSizeY];
+                for (int x = 0; x < m_scene.RegionInfo.RegionSizeX; x++)
+                {
+                    for (int y = 0; y < m_scene.RegionInfo.RegionSizeY; y++)
+                    {
+                        waterMap[y * m_scene.RegionInfo.RegionSizeX + x] = (float)height;
+                    }
+                }
+            }
+            else
+                waterMap = m_waterChannel.GetFloatsSerialised(m_scene);
+            m_scene.SceneGraph.PhysicsScene.SetWaterLevel(waterMap);
+        }
+
+        /// <summary>
+        /// Installs terrain brush hook to IClientAPI
+        /// </summary>
+        /// <param name="client"></param>
+        private void EventManager_OnNewClient(IClientAPI client)
+        {
+            client.OnModifyTerrain += client_OnModifyTerrain;
+            client.OnBakeTerrain += client_OnBakeTerrain;
+            client.OnLandUndo += client_OnLandUndo;
+            client.OnGodlikeMessage += client_onGodlikeMessage;
+            client.OnUnackedTerrain += client_OnUnackedTerrain;
+            client.OnRegionHandShakeReply += SendLayerData;
+
+            //Add them to the cache
+            lock (m_terrainPatchesSent)
+            {
+                if (!m_terrainPatchesSent.ContainsKey(client.AgentId))
+                {
+                    ScenePresence agent = m_scene.GetScenePresence(client.AgentId);
+                    if (agent != null && agent.IsChildAgent)
+                    {
+                        //If the avatar is a child agent, we need to send the terrain data initially
+                        EventManager_OnSignificantClientMovement(client);
+                    }
+                }
+            }
+        }
+
+        private void OnClosingClient(IClientAPI client)
+        {
+            client.OnModifyTerrain -= client_OnModifyTerrain;
+            client.OnBakeTerrain -= client_OnBakeTerrain;
+            client.OnLandUndo -= client_OnLandUndo;
+            client.OnGodlikeMessage -= client_onGodlikeMessage;
+            client.OnUnackedTerrain -= client_OnUnackedTerrain;
+            client.OnRegionHandShakeReply -= SendLayerData;
+
+            //Remove them from the cache
+            lock (m_terrainPatchesSent)
+            {
+                m_terrainPatchesSent.Remove(client.AgentId);
+            }
+        }
+
+        /// <summary>
+        /// Send the region heightmap to the client
+        /// </summary>
+        /// <param name="RemoteClient">Client to send to</param>
+        public void SendLayerData(IClientAPI RemoteClient)
+        {
+            ScenePresence presence = m_scene.GetScenePresence(RemoteClient.AgentId);
+            if (!m_sendTerrainUpdatesByViewDistance)
+            {
+                //Default way, send the full terrain at once
+                RemoteClient.SendLayerData(m_channel.GetFloatsSerialised(RemoteClient.Scene));
+            }
+            else
+            {
+                //Send only what the client can see,
+                //  but the client isn't loaded yet, wait until they get set up
+                //  The first agent update they send will trigger the DrawDistanceChanged event and send the land
+            }
+        }
+
+        void AuroraEventManager_OnGenericEvent(string FunctionName, object parameters)
+        {
+            if (FunctionName == "DrawDistanceChanged" || FunctionName == "SignficantCameraMovement")
+            {
+                SendTerrainUpdatesForClient((ScenePresence)parameters);
+            }
+        }
+
+        void EventManager_OnSignificantClientMovement(IClientAPI remote_client)
+        {
+            ScenePresence presence = m_scene.GetScenePresence(remote_client.AgentId);
+            SendTerrainUpdatesForClient(presence);
+        }
+
+        void OnNewPresence(ScenePresence presence)
+        {
+            SendTerrainUpdatesForClient(presence);
+        }
+
+        protected void SendTerrainUpdatesForClient(ScenePresence presence)
+        {
+            if (!m_sendTerrainUpdatesByViewDistance)
+                return;
+
+            if (presence == null)
+                return;
+
+            if (!m_terrainPatchesSent.ContainsKey(presence.UUID))
+            {
+                int xSize = m_scene.RegionInfo.RegionSizeX != int.MaxValue ? m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize : Constants.RegionSize / Constants.TerrainPatchSize;
+                int ySize = m_scene.RegionInfo.RegionSizeX != int.MaxValue ? m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize : Constants.RegionSize / Constants.TerrainPatchSize;
+                m_terrainPatchesSent.Add(presence.UUID, new bool[xSize,
+                    ySize]);
+            }
+
+            List<int> xs = new List<int>();
+            List<int> ys = new List<int>();
+            if (m_scene.RegionInfo.RegionSizeX != int.MaxValue)
+            {
+                for (int x = 0; x <
+                    m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize; x++) //Make sure that we don't send past what viewers like
+                {
+                    for (int y = 0; y <
+                        m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize; y++) //Make sure that we don't send past what viewers like
+                    {
+                        //Need to make sure we don't send the same ones over and over
+                        if (!m_terrainPatchesSent[presence.UUID][x, y])
+                        {
+                            //Check which has less distance, camera or avatar position, both have to be done
+                            double posdistance = Util.GetFlatDistanceTo(presence.AbsolutePosition,
+                                new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, 0));
+                            double camdistance = Util.GetFlatDistanceTo(presence.CameraPosition,
+                                new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, 0));
+                            //Take the smaller of the two
+                            double distance = posdistance > camdistance ? camdistance : posdistance;
+                            if (distance < presence.DrawDistance + 35) //Its not a radius, its a diameter and we add 35 so that it doesn't look like it cuts off
+                            {
+                                //They can see it, send it ot them
+                                m_terrainPatchesSent[presence.UUID][x, y] = true;
+                                xs.Add(x);
+                                ys.Add(y);
+                                //Wait and send them all at once
+                                //presence.ControllingClient.SendLayerData(x, y, serializedMap);
+                            }
+                        }
+                    }
+                }
+            }
+            else
+            {
+                ///MegaRegion!
+                for (int x = 0; x <
+                    Constants.RegionSize / Constants.TerrainPatchSize; x++)
+                {
+                    for (int y = 0; y <
+                        Constants.RegionSize / Constants.TerrainPatchSize; y++)
+                    {
+                        //Need to make sure we don't send the same ones over and over
+                        if (!m_terrainPatchesSent[presence.UUID][x, y])
+                        {
+                            double distance = Util.GetFlatDistanceTo(presence.AbsolutePosition,
+                                new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, 0));
+                            if (distance < presence.DrawDistance + 35) //Its not a radius, its a diameter and we add 35 so that it doesn't look like it cuts off
+                            {
+                                //They can see it, send it ot them
+                                m_terrainPatchesSent[presence.UUID][x, y] = true;
+                                xs.Add(x);
+                                ys.Add(y);
+                            }
+                        }
+                    }
+                }
+            }
+            float[] serializedTerrainMap = m_channel.GetFloatsSerialised(m_scene);
+            //Send all the terrain patches at once
+            presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), serializedTerrainMap, TerrainPatch.LayerType.Land);
+
+            if (m_use3DWater)
+            {
+                float[] serializedWaterMap = m_waterChannel.GetFloatsSerialised(m_scene);
+                //Send all the water patches at once
+                presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), serializedWaterMap, TerrainPatch.LayerType.Water);
+            }
+        }
+
+        /// <summary>
+        /// Store the terrain in the persistant data store
+        /// </summary>
+        public void SaveTerrain()
+        {
+            m_scene.SimulationDataService.StoreTerrain(m_channel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+        }
+
+        /// <summary>
+        /// Store the terrain in the persistant data store
+        /// </summary>
+        public void SaveWater()
+        {
+            m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+        }
+
+        /// <summary>
+        /// Reset the terrain of this region to the default
+        /// </summary>
+        public void ResetTerrain()
+        {
+            TerrainChannel channel = new TerrainChannel(m_scene);
+            m_channel = channel;
+            SaveRevertTerrain(channel);
+            m_scene.RegisterModuleInterface<ITerrainChannel>(m_channel);
+            CheckForTerrainUpdates(false, true, false);
+        }
+
+        /// <summary>
+        /// Reset the terrain of this region to the default
+        /// </summary>
+        public void ResetWater()
+        {
+            TerrainChannel channel = new TerrainChannel(m_scene);
+            m_waterChannel = channel;
+            SaveRevertWater(m_waterChannel);
+            CheckForTerrainUpdates(false, true, true);
+        }
+
+        /// <summary>
+        /// Store the revert terrain in the persistant data store
+        /// </summary>
+        public void SaveRevertTerrain(ITerrainChannel channel)
+        {
+            m_scene.SimulationDataService.StoreTerrain(m_revert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+        }
+
+        /// <summary>
+        /// Store the revert terrain in the persistant data store
+        /// </summary>
+        public void SaveRevertWater(ITerrainChannel channel)
+        {
+            m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
         }

         /// <summary>
         /// Loads the World Revert heightmap
         /// </summary>
         public void LoadRevertMap()
-        {
-            try
-            {
-                double[,] map = m_scene.SimulationDataService.LoadTerrain(m_scene.RegionInfo.RegionID, true, m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY);
-                if (map == null)
-                {
-                    m_revert = m_channel.MakeCopy();
-
-                    m_scene.SimulationDataService.StoreTerrain(m_revert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
-                }
-                else
-                {
-                    m_revert = new TerrainChannel(map, m_scene);
-                }
-            }
-            catch (IOException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
-                // Non standard region size.    If there's an old terrain in the database, it might read past the buffer
-                if (m_scene.RegionInfo.RegionSizeX != Constants.RegionSize || m_scene.RegionInfo.RegionSizeY != Constants.RegionSize)
-                {
-                    m_revert = m_channel.MakeCopy();
-
-                    m_scene.SimulationDataService.StoreTerrain(m_revert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
-                }
-            }
-            catch (IndexOutOfRangeException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
-                m_revert = m_channel.MakeCopy();
-
-                m_scene.SimulationDataService.StoreTerrain(m_revert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
-            }
-            catch (ArgumentOutOfRangeException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
-                m_revert = m_channel.MakeCopy();
-
-                m_scene.SimulationDataService.StoreTerrain(m_revert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
-            }
-            catch (Exception e)
-            {
-                m_log.Warn("[TERRAIN]: Scene.cs: LoadWorldMap() - Failed with exception " + e.ToString());
-            }
-        }
-
-        /// <summary>
-        /// Loads the World Revert heightmap
-        /// </summary>
-        public void LoadRevertWaterMap()
-        {
-            try
-            {
-                double[,] map = m_scene.SimulationDataService.LoadWater(m_scene.RegionInfo.RegionID, true, m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY);
-                if (map == null)
-                {
-                    m_waterRevert = m_waterChannel.MakeCopy();
-
-                    m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
-                }
-                else
-                {
-                    m_waterRevert = new TerrainChannel(map, m_scene);
-                }
-            }
-            catch (IOException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadRevertWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
-                // Non standard region size.    If there's an old terrain in the database, it might read past the buffer
-                if (m_scene.RegionInfo.RegionSizeX != Constants.RegionSize || m_scene.RegionInfo.RegionSizeY != Constants.RegionSize)
-                {
-                    m_waterRevert = m_waterChannel.MakeCopy();
-
-                    m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
-                }
-            }
-            catch (IndexOutOfRangeException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadRevertWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
-                m_waterRevert = m_waterChannel.MakeCopy();
-
-                m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
-            }
-            catch (ArgumentOutOfRangeException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadRevertWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
-                m_waterRevert = m_waterChannel.MakeCopy();
-
-                m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
-            }
-            catch (Exception e)
-            {
-                m_log.Warn("[TERRAIN]: LoadRevertWaterMap() - Failed with exception " + e.ToString());
-                m_waterRevert = m_waterChannel.MakeCopy();
-
-                m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
-            }
+        {
+            try
+            {
+                double[,] map = m_scene.SimulationDataService.LoadTerrain(m_scene.RegionInfo.RegionID, true, m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY);
+                if (map == null)
+                {
+                    m_revert = m_channel.MakeCopy();
+
+                    m_scene.SimulationDataService.StoreTerrain(m_revert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+                }
+                else
+                {
+                    m_revert = new TerrainChannel(map, m_scene);
+                }
+            }
+            catch (IOException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
+                // Non standard region size.    If there's an old terrain in the database, it might read past the buffer
+                if (m_scene.RegionInfo.RegionSizeX != Constants.RegionSize || m_scene.RegionInfo.RegionSizeY != Constants.RegionSize)
+                {
+                    m_revert = m_channel.MakeCopy();
+
+                    m_scene.SimulationDataService.StoreTerrain(m_revert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+                }
+            }
+            catch (IndexOutOfRangeException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
+                m_revert = m_channel.MakeCopy();
+
+                m_scene.SimulationDataService.StoreTerrain(m_revert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+            }
+            catch (ArgumentOutOfRangeException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
+                m_revert = m_channel.MakeCopy();
+
+                m_scene.SimulationDataService.StoreTerrain(m_revert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+            }
+            catch (Exception e)
+            {
+                m_log.Warn("[TERRAIN]: Scene.cs: LoadWorldMap() - Failed with exception " + e.ToString());
+            }
+        }
+
+        /// <summary>
+        /// Loads the World Revert heightmap
+        /// </summary>
+        public void LoadRevertWaterMap()
+        {
+            try
+            {
+                double[,] map = m_scene.SimulationDataService.LoadWater(m_scene.RegionInfo.RegionID, true, m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY);
+                if (map == null)
+                {
+                    m_waterRevert = m_waterChannel.MakeCopy();
+
+                    m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+                }
+                else
+                {
+                    m_waterRevert = new TerrainChannel(map, m_scene);
+                }
+            }
+            catch (IOException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadRevertWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
+                // Non standard region size.    If there's an old terrain in the database, it might read past the buffer
+                if (m_scene.RegionInfo.RegionSizeX != Constants.RegionSize || m_scene.RegionInfo.RegionSizeY != Constants.RegionSize)
+                {
+                    m_waterRevert = m_waterChannel.MakeCopy();
+
+                    m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+                }
+            }
+            catch (IndexOutOfRangeException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadRevertWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
+                m_waterRevert = m_waterChannel.MakeCopy();
+
+                m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+            }
+            catch (ArgumentOutOfRangeException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadRevertWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
+                m_waterRevert = m_waterChannel.MakeCopy();
+
+                m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+            }
+            catch (Exception e)
+            {
+                m_log.Warn("[TERRAIN]: LoadRevertWaterMap() - Failed with exception " + e.ToString());
+                m_waterRevert = m_waterChannel.MakeCopy();
+
+                m_scene.SimulationDataService.StoreWater(m_waterRevert.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, true);
+            }
         }

         /// <summary>
         /// Loads the World heightmap
         /// </summary>
         public void LoadWorldHeightmap()
-        {
-            try
-            {
-                double[,] map = m_scene.SimulationDataService.LoadTerrain(m_scene.RegionInfo.RegionID, false, m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY);
-                if (map == null)
-                {
-                    m_log.Info("[TERRAIN]: No default terrain. Generating a new terrain.");
-                    m_channel = new TerrainChannel(m_scene);
-
-                    m_scene.SimulationDataService.StoreTerrain(m_channel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-                }
-                else
-                {
-                    m_channel = new TerrainChannel(map, m_scene);
-                }
-            }
-            catch (IOException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
-                // Non standard region size.    If there's an old terrain in the database, it might read past the buffer
-                if (m_scene.RegionInfo.RegionSizeX != Constants.RegionSize || m_scene.RegionInfo.RegionSizeY != Constants.RegionSize)
-                {
-                    m_channel = new TerrainChannel(m_scene);
-
-                    m_scene.SimulationDataService.StoreTerrain(m_channel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-                }
-            }
-            catch (IndexOutOfRangeException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
-                m_channel = new TerrainChannel(m_scene);
-
-                m_scene.SimulationDataService.StoreTerrain(m_channel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-            }
-            catch (ArgumentOutOfRangeException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
-                m_channel = new TerrainChannel(m_scene);
-
-                m_scene.SimulationDataService.StoreTerrain(m_channel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-            }
-            catch (Exception e)
-            {
-                m_log.Warn("[TERRAIN]: Scene.cs: LoadWorldMap() - Failed with exception " + e.ToString());
-            }
+        {
+            try
+            {
+                double[,] map = m_scene.SimulationDataService.LoadTerrain(m_scene.RegionInfo.RegionID, false, m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY);
+                if (map == null)
+                {
+                    m_log.Info("[TERRAIN]: No default terrain. Generating a new terrain.");
+                    m_channel = new TerrainChannel(m_scene);
+
+                    m_scene.SimulationDataService.StoreTerrain(m_channel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+                }
+                else
+                {
+                    m_channel = new TerrainChannel(map, m_scene);
+                }
+            }
+            catch (IOException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
+                // Non standard region size.    If there's an old terrain in the database, it might read past the buffer
+                if (m_scene.RegionInfo.RegionSizeX != Constants.RegionSize || m_scene.RegionInfo.RegionSizeY != Constants.RegionSize)
+                {
+                    m_channel = new TerrainChannel(m_scene);
+
+                    m_scene.SimulationDataService.StoreTerrain(m_channel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+                }
+            }
+            catch (IndexOutOfRangeException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
+                m_channel = new TerrainChannel(m_scene);
+
+                m_scene.SimulationDataService.StoreTerrain(m_channel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+            }
+            catch (ArgumentOutOfRangeException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadWorldMap() - Failed with exception " + e.ToString() + " Regenerating");
+                m_channel = new TerrainChannel(m_scene);
+
+                m_scene.SimulationDataService.StoreTerrain(m_channel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+            }
+            catch (Exception e)
+            {
+                m_log.Warn("[TERRAIN]: Scene.cs: LoadWorldMap() - Failed with exception " + e.ToString());
+            }
             LoadRevertMap();
             m_scene.RegisterModuleInterface<ITerrainChannel>(m_channel);
-        }
-
-        /// <summary>
-        /// Loads the World heightmap
-        /// </summary>
-        public void LoadWorldWaterMap()
-        {
-            if (!m_use3DWater)
-                return;
-            try
-            {
-                double[,] map = m_scene.SimulationDataService.LoadWater(m_scene.RegionInfo.RegionID, false, m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY);
-                if (map == null)
-                {
-                    m_log.Info("[TERRAIN]: No default water. Generating a new water.");
-                    m_waterChannel = new TerrainChannel(m_scene);
-
-                    m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-                }
-                else
-                {
-                    m_channel = new TerrainChannel(map, m_scene);
-                }
-            }
-            catch (IOException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadWorldWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
-                // Non standard region size.    If there's an old terrain in the database, it might read past the buffer
-                if (m_scene.RegionInfo.RegionSizeX != Constants.RegionSize || m_scene.RegionInfo.RegionSizeY != Constants.RegionSize)
-                {
-                    m_waterChannel = new TerrainChannel(m_scene);
-
-                    m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-                }
-            }
-            catch (IndexOutOfRangeException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadWorldWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
-                m_waterChannel = new TerrainChannel(m_scene);
-
-                m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-            }
-            catch (ArgumentOutOfRangeException e)
-            {
-                m_log.Warn("[TERRAIN]: LoadWorldWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
-                m_waterChannel = new TerrainChannel(m_scene);
-
-                m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-            }
-            catch (Exception e)
-            {
-                m_log.Warn("[TERRAIN]: Scene.cs: LoadWorldMap() - Failed with exception " + e.ToString());
-                m_waterChannel = new TerrainChannel(m_scene);
-
-                m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
-            }
-            LoadRevertWaterMap();
+        }
+
+        /// <summary>
+        /// Loads the World heightmap
+        /// </summary>
+        public void LoadWorldWaterMap()
+        {
+            if (!m_use3DWater)
+                return;
+            try
+            {
+                double[,] map = m_scene.SimulationDataService.LoadWater(m_scene.RegionInfo.RegionID, false, m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY);
+                if (map == null)
+                {
+                    m_log.Info("[TERRAIN]: No default water. Generating a new water.");
+                    m_waterChannel = new TerrainChannel(m_scene);
+
+                    m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+                }
+                else
+                {
+                    m_channel = new TerrainChannel(map, m_scene);
+                }
+            }
+            catch (IOException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadWorldWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
+                // Non standard region size.    If there's an old terrain in the database, it might read past the buffer
+                if (m_scene.RegionInfo.RegionSizeX != Constants.RegionSize || m_scene.RegionInfo.RegionSizeY != Constants.RegionSize)
+                {
+                    m_waterChannel = new TerrainChannel(m_scene);
+
+                    m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+                }
+            }
+            catch (IndexOutOfRangeException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadWorldWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
+                m_waterChannel = new TerrainChannel(m_scene);
+
+                m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+            }
+            catch (ArgumentOutOfRangeException e)
+            {
+                m_log.Warn("[TERRAIN]: LoadWorldWaterMap() - Failed with exception " + e.ToString() + " Regenerating");
+                m_waterChannel = new TerrainChannel(m_scene);
+
+                m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+            }
+            catch (Exception e)
+            {
+                m_log.Warn("[TERRAIN]: Scene.cs: LoadWorldMap() - Failed with exception " + e.ToString());
+                m_waterChannel = new TerrainChannel(m_scene);
+
+                m_scene.SimulationDataService.StoreWater(m_waterChannel.GetDoubles(m_scene), m_scene.RegionInfo.RegionID, false);
+            }
+            LoadRevertWaterMap();
         }

         public void UndoTerrain(ITerrainChannel channel)
@@ -682,35 +682,35 @@
                         try
                         {
                             ITerrainChannel channel = loader.Value.LoadFile(filename);
-                            channel.Scene = m_scene;
-                            if (m_channel.Height == channel.Height &&
-                                    m_channel.Width == channel.Width)
-                            {
-                                m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height);
-                            }
-                            else
-                            {
-                                //Make sure it is in bounds
-                                if ((offsetX + channel.Width) > m_channel.Width ||
-                                        (offsetY + channel.Height) > m_channel.Height)
-                                {
-                                    m_log.Error("[TERRAIN]: Unable to load heightmap, the terrain you have given is larger than the current region.");
-                                    return;
-                                }
-                                else
-                                {
-                                    //Merge the terrains together at the specified offset
-                                    for (int x = offsetX; x < offsetX + channel.Width; x++)
-                                    {
-                                        for (int y = offsetY; y < offsetY + channel.Height; y++)
-                                        {
-                                            m_channel[x, y] = channel[x - offsetX, y - offsetY];
-                                        }
-                                    }
-                                }
-                            }
-                            m_channel = channel;
-                            m_scene.RegisterModuleInterface<ITerrainChannel>(m_channel);
+                            channel.Scene = m_scene;
+                            if (m_channel.Height == channel.Height &&
+                                    m_channel.Width == channel.Width)
+                            {
+                                m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height);
+                            }
+                            else
+                            {
+                                //Make sure it is in bounds
+                                if ((offsetX + channel.Width) > m_channel.Width ||
+                                        (offsetY + channel.Height) > m_channel.Height)
+                                {
+                                    m_log.Error("[TERRAIN]: Unable to load heightmap, the terrain you have given is larger than the current region.");
+                                    return;
+                                }
+                                else
+                                {
+                                    //Merge the terrains together at the specified offset
+                                    for (int x = offsetX; x < offsetX + channel.Width; x++)
+                                    {
+                                        for (int y = offsetY; y < offsetY + channel.Height; y++)
+                                        {
+                                            m_channel[x, y] = channel[x - offsetX, y - offsetY];
+                                        }
+                                    }
+                                }
+                            }
+                            m_channel = channel;
+                            m_scene.RegisterModuleInterface<ITerrainChannel>(m_channel);
                             UpdateRevertMap();
                         }
                         catch (NotImplementedException)
@@ -800,18 +800,18 @@
                             ITerrainChannel channel = loader.Value.LoadStream(stream);
                             if (channel != null)
                             {
-                                channel.Scene = m_scene;
-                                if (m_channel.Height == channel.Height &&
-                                    m_channel.Width == channel.Width)
-                                {
-                                    m_channel = channel;
-                                    m_scene.RegisterModuleInterface<ITerrainChannel>(m_channel);
-                                    UpdateRevertMap();
-                                }
-                                else
-                                {
-                                    m_log.Warn("[TerrainModule]: Got request to load terrain that was not the correct size!");
-                                    return;
+                                channel.Scene = m_scene;
+                                if (m_channel.Height == channel.Height &&
+                                    m_channel.Width == channel.Width)
+                                {
+                                    m_channel = channel;
+                                    m_scene.RegisterModuleInterface<ITerrainChannel>(m_channel);
+                                    UpdateRevertMap();
+                                }
+                                else
+                                {
+                                    m_log.Warn("[TerrainModule]: Got request to load terrain that was not the correct size!");
+                                    return;
                                 }
                             }
                         }
@@ -830,72 +830,72 @@
             }
             m_log.Error("[TERRAIN]: Unable to load heightmap, no file loader available for that format.");
             throw new TerrainException(String.Format("unable to load heightmap from file {0}: no loader available for that format", filename));
-        }
-
-        /// <summary>
-        /// Loads a terrain file from a stream and installs it in the scene.
-        /// </summary>
-        /// <param name="filename">Filename to terrain file. Type is determined by extension.</param>
-        /// <param name="stream"></param>
-        public void LoadFromStream(string filename, Stream stream, int offsetX, int offsetY)
-        {
-            foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders)
-            {
-                if (filename.EndsWith(loader.Key))
-                {
-                    lock (m_scene)
-                    {
-                        try
-                        {
-                            ITerrainChannel channel = loader.Value.LoadStream(stream);
-                            if (channel != null)
-                            {
-                                channel.Scene = m_scene;
-                                if (m_channel.Height == channel.Height &&
-                                    m_channel.Width == channel.Width)
-                                {
-                                    m_channel = channel;
-                                    m_scene.RegisterModuleInterface<ITerrainChannel>(m_channel);
-                                }
-                                else
-                                {
-                                    //Make sure it is in bounds
-                                    if ((offsetX + channel.Width) > m_channel.Width ||
-                                            (offsetY + channel.Height) > m_channel.Height)
-                                    {
-                                        m_log.Error("[TERRAIN]: Unable to load heightmap, the terrain you have given is larger than the current region.");
-                                        return;
-                                    }
-                                    else
-                                    {
-                                        //Merge the terrains together at the specified offset
-                                        for (int x = offsetX; x < offsetX + channel.Width; x++)
-                                        {
-                                            for (int y = offsetY; y < offsetY + channel.Height; y++)
-                                            {
-                                                m_channel[x, y] = channel[x - offsetX, y - offsetY];
-                                            }
-                                        }
-                                    }
-                                }
-                                UpdateRevertMap();
-                            }
-                        }
-                        catch (NotImplementedException)
-                        {
-                            m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value +
-                                        " parser does not support file loading. (May be save only)");
-                            throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value));
-                        }
-                    }
-
-                    CheckForTerrainUpdates();
-                    m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
-                    return;
-                }
-            }
-            m_log.Error("[TERRAIN]: Unable to load heightmap, no file loader available for that format.");
-            throw new TerrainException(String.Format("unable to load heightmap from file {0}: no loader available for that format", filename));
+        }
+
+        /// <summary>
+        /// Loads a terrain file from a stream and installs it in the scene.
+        /// </summary>
+        /// <param name="filename">Filename to terrain file. Type is determined by extension.</param>
+        /// <param name="stream"></param>
+        public void LoadFromStream(string filename, Stream stream, int offsetX, int offsetY)
+        {
+            foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders)
+            {
+                if (filename.EndsWith(loader.Key))
+                {
+                    lock (m_scene)
+                    {
+                        try
+                        {
+                            ITerrainChannel channel = loader.Value.LoadStream(stream);
+                            if (channel != null)
+                            {
+                                channel.Scene = m_scene;
+                                if (m_channel.Height == channel.Height &&
+                                    m_channel.Width == channel.Width)
+                                {
+                                    m_channel = channel;
+                                    m_scene.RegisterModuleInterface<ITerrainChannel>(m_channel);
+                                }
+                                else
+                                {
+                                    //Make sure it is in bounds
+                                    if ((offsetX + channel.Width) > m_channel.Width ||
+                                            (offsetY + channel.Height) > m_channel.Height)
+                                    {
+                                        m_log.Error("[TERRAIN]: Unable to load heightmap, the terrain you have given is larger than the current region.");
+                                        return;
+                                    }
+                                    else
+                                    {
+                                        //Merge the terrains together at the specified offset
+                                        for (int x = offsetX; x < offsetX + channel.Width; x++)
+                                        {
+                                            for (int y = offsetY; y < offsetY + channel.Height; y++)
+                                            {
+                                                m_channel[x, y] = channel[x - offsetX, y - offsetY];
+                                            }
+                                        }
+                                    }
+                                }
+                                UpdateRevertMap();
+                            }
+                        }
+                        catch (NotImplementedException)
+                        {
+                            m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value +
+                                        " parser does not support file loading. (May be save only)");
+                            throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value));
+                        }
+                    }
+
+                    CheckForTerrainUpdates();
+                    m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
+                    return;
+                }
+            }
+            m_log.Error("[TERRAIN]: Unable to load heightmap, no file loader available for that format.");
+            throw new TerrainException(String.Format("unable to load heightmap from file {0}: no loader available for that format", filename));
         }

         private static Stream URIFetch(Uri uri)
@@ -1069,25 +1069,25 @@
             m_loaders[".png"] = new PNG();
             m_loaders[".gif"] = new GIF();
             m_loaders[".tif"] = new TIFF();
-            m_loaders[".tiff"] = m_loaders[".tif"];
-        }
-
-        /// <summary>
-        /// Saves the current state of the region into the revert map buffer.
-        /// </summary>
-        public void UpdateRevertWaterMap()
-        {
-            m_waterRevert = m_waterChannel.MakeCopy();
-            SaveRevertTerrain(m_waterRevert);
-        }
-
-        /// <summary>
-        /// Saves the current state of the region into the revert map buffer.
-        /// </summary>
-        public void UpdateRevertMap()
-        {
-            m_revert = m_channel.MakeCopy();
-            SaveRevertTerrain(m_revert);
+            m_loaders[".tiff"] = m_loaders[".tif"];
+        }
+
+        /// <summary>
+        /// Saves the current state of the region into the revert map buffer.
+        /// </summary>
+        public void UpdateRevertWaterMap()
+        {
+            m_waterRevert = m_waterChannel.MakeCopy();
+            SaveRevertTerrain(m_waterRevert);
+        }
+
+        /// <summary>
+        /// Saves the current state of the region into the revert map buffer.
+        /// </summary>
+        public void UpdateRevertMap()
+        {
+            m_revert = m_channel.MakeCopy();
+            SaveRevertTerrain(m_revert);
         }

         /// <summary>
@@ -1113,7 +1113,7 @@
                         lock (m_scene)
                         {
                             ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY,
-                                                                            fileWidth, fileHeight,
+                                                                            fileWidth, fileHeight,
                                                                             m_scene.RegionInfo.RegionSizeX,
                                                                             m_scene.RegionInfo.RegionSizeY);
                             channel.Scene = m_scene;
@@ -1126,7 +1126,7 @@
                 }
             }
         }
-
+
         void client_onGodlikeMessage(IClientAPI client, UUID requester, string Method, List<string> Parameters)
         {
             if (!m_scene.Permissions.IsGod(client.AgentId))
@@ -1166,56 +1166,60 @@
         /// If it has been modified, every all the terrain patches are sent to the client.
         /// If the call is asked to respect the estate settings for terrain_raise_limit and
         /// terrain_lower_limit, it will clamp terrain updates between these values
-        /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces
-        /// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param>
-        /// <param name="forceSendOfTerrainInfo">force send terrain</param>
-        /// <param name="isWater">Check water or terrain</param>
+        /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces
+        /// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param>
+        /// <param name="forceSendOfTerrainInfo">force send terrain</param>
+        /// <param name="isWater">Check water or terrain</param>
         /// </summary>
         private void CheckForTerrainUpdates(bool respectEstateSettings, bool forceSendOfTerrainInfo, bool isWater)
-        {
+        {
             ITerrainChannel channel = isWater ? m_waterChannel : m_channel;
-            bool shouldTaint = false;
-
-            // if we should respect the estate settings then
-            // fixup and height deltas that don't respect them
-            if(respectEstateSettings)
-                LimitChannelChanges();
-            else if (!forceSendOfTerrainInfo)
-                LimitMaxTerrain();
-
-            List<int> xs = new List<int>();
-            List<int> ys = new List<int>();
+            bool shouldTaint = false;
+
+            // if we should respect the estate settings then
+            // fixup and height deltas that don't respect them
+            if(respectEstateSettings)
+                LimitChannelChanges();
+            else if (!forceSendOfTerrainInfo)
+                LimitMaxTerrain();
+
+            List<int> xs = new List<int>();
+            List<int> ys = new List<int>();
             for (int x = 0; x < channel.Width; x += Constants.TerrainPatchSize)
-            {
+            {
                 for (int y = 0; y < channel.Height; y += Constants.TerrainPatchSize)
-                {
+                {
                     if (channel.Tainted(x, y) || forceSendOfTerrainInfo)
-                    {
-                        xs.Add(x / Constants.TerrainPatchSize);
+                    {
+                        xs.Add(x / Constants.TerrainPatchSize);
                         ys.Add(y / Constants.TerrainPatchSize);
                         shouldTaint = true;
                     }
                 }
             }
-            if (shouldTaint || forceSendOfTerrainInfo)
-                QueueTerrainUpdate();
-
-            float[] serialised = channel.GetFloatsSerialised(m_scene);
-            foreach (ScenePresence presence in m_scene.ScenePresences)
-            {
-                presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), serialised, TerrainPatch.LayerType.Land);
-            }
-        }
-
-        private bool LimitMaxTerrain(int xStart, int yStart)
+            if (shouldTaint || forceSendOfTerrainInfo)
+                QueueTerrainUpdate();
+
+            float[] serialised = channel.GetFloatsSerialised(m_scene);
+            foreach (ScenePresence presence in m_scene.ScenePresences)
+            {
+                for(int i = 0; i < xs.Count; i++)
+                {
+                     m_terrainPatchesSent[presence.UUID][xs[i], ys[i]] = false;
+                }
+                SendTerrainUpdatesForClient(presence);
+            }
+        }
+
+        private bool LimitMaxTerrain()
         {
             bool changesLimited = false;

             // loop through the height map for this patch and compare it against
             // the revert map
-            for (int x = xStart; x < xStart + Constants.TerrainPatchSize; x++)
-            {
-                for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++)
+            for (int x = 0; x < m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize; x++)
+            {
+                for (int y = 0; y < m_scene.RegionInfo.RegionSizeY / +Constants.TerrainPatchSize; y++)
                 {
                     double requestedHeight = m_channel[x, y];

@@ -1233,72 +1237,6 @@
             }

             return changesLimited;
-        }
-
-        private bool LimitMaxTerrain()
-        {
-            bool changesLimited = false;
-
-            // loop through the height map for this patch and compare it against
-            // the revert map
-            for (int x = 0; x < m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize; x++)
-            {
-                for (int y = 0; y < m_scene.RegionInfo.RegionSizeY / +Constants.TerrainPatchSize; y++)
-                {
-                    double requestedHeight = m_channel[x, y];
-
-                    if (requestedHeight > MAX_HEIGHT)
-                    {
-                        m_channel[x, y] = MAX_HEIGHT;
-                        changesLimited = true;
-                    }
-                    else if (requestedHeight < MIN_HEIGHT)
-                    {
-                        m_channel[x, y] = MIN_HEIGHT; //as lower is a -ve delta
-                        changesLimited = true;
-                    }
-                }
-            }
-
-            return changesLimited;
-        }
-
-        /// <summary>
-        /// Checks to see height deltas in the tainted terrain patch at xStart ,yStart
-        /// are all within the current estate limits
-        /// <returns>true if changes were limited, false otherwise</returns>
-        /// </summary>
-        private bool LimitChannelChanges()
-        {
-            bool changesLimited = false;
-            double minDelta = m_scene.RegionInfo.RegionSettings.TerrainLowerLimit;
-            double maxDelta = m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit;
-
-            // loop through the height map for this patch and compare it against
-            // the revert map
-            for (int x = 0; x < m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize; x++)
-            {
-                for (int y = 0; y < m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize; y++)
-                {
-
-                    double requestedHeight = m_channel[x, y];
-                    double bakedHeight = m_revert[x, y];
-                    double requestedDelta = requestedHeight - bakedHeight;
-
-                    if (requestedDelta > maxDelta)
-                    {
-                        m_channel[x, y] = bakedHeight + maxDelta;
-                        changesLimited = true;
-                    }
-                    else if (requestedDelta < minDelta)
-                    {
-                        m_channel[x, y] = bakedHeight + minDelta; //as lower is a -ve delta
-                        changesLimited = true;
-                    }
-                }
-            }
-
-            return changesLimited;
         }

         /// <summary>
@@ -1306,7 +1244,7 @@
         /// are all within the current estate limits
         /// <returns>true if changes were limited, false otherwise</returns>
         /// </summary>
-        private bool LimitChannelChanges(int xStart, int yStart)
+        private bool LimitChannelChanges()
         {
             bool changesLimited = false;
             double minDelta = m_scene.RegionInfo.RegionSettings.TerrainLowerLimit;
@@ -1314,9 +1252,9 @@

             // loop through the height map for this patch and compare it against
             // the revert map
-            for (int x = xStart; x < xStart + Constants.TerrainPatchSize; x++)
-            {
-                for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++)
+            for (int x = 0; x < m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize; x++)
+            {
+                for (int y = 0; y < m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize; y++)
                 {

                     double requestedHeight = m_channel[x, y];
@@ -1372,7 +1310,7 @@
                                             float north, float west, float south, float east, UUID agentId, float BrushSize)
         {
             bool god = m_scene.Permissions.IsGod(user);
-            bool allowed = false;
+            bool allowed = false;
             bool isWater = ((action & 512) == 512); //512 means its modifying water
             if (north == south && east == west)
             {
@@ -1407,15 +1345,15 @@
                     {
                         StoreUndoState();
                         m_painteffects[(StandardTerrainEffects) action].PaintEffect(
-                            m_channel, allowMask, west, south, height, size, seconds, BrushSize, m_scenes);
-
-                        //revert changes outside estate limits
+                            m_channel, allowMask, west, south, height, size, seconds, BrushSize, m_scenes);
+
+                        //revert changes outside estate limits
                         CheckForTerrainUpdates(!god, false, false);
                     }
                 }
                 else
                 {
-                    m_log.Debug("Unknown terrain brush type " + action);
+                    m_log.Warn("Unknown terrain brush type " + action);
                 }
             }
             else
@@ -1449,15 +1387,15 @@
                     {
                         StoreUndoState();
                         m_floodeffects[(StandardTerrainEffects) action].FloodEffect(
-                            m_channel, fillArea, size);
-
-                        //revert changes outside estate limits
+                            m_channel, fillArea, size);
+
+                        //revert changes outside estate limits
                         CheckForTerrainUpdates(!god, false, false);
                     }
                 }
                 else
                 {
-                    m_log.Debug("Unknown terrain flood type " + action);
+                    m_log.Warn("Unknown terrain flood type " + action);
                 }
             }
         }
@@ -1523,24 +1461,24 @@

         private void InterfaceLoadFile(string module, string[] cmd)
         {
-            List<TerrainModule> m = FindModuleForScene(MainConsole.Instance.ConsoleScene);
-            int offsetX = 0;
-            int offsetY = 0;
-
-            int i = 0;
-            foreach (string param in cmd)
-            {
-                if (param.StartsWith("OffsetX"))
-                {
-                    string retVal = param.Remove(0, 8);
-                    int.TryParse(retVal, out offsetX);
-                }
-                else if (param.StartsWith("OffsetY"))
-                {
-                    string retVal = param.Remove(0, 8);
-                    int.TryParse(retVal, out offsetY);
-                }
-                i++;
+            List<TerrainModule> m = FindModuleForScene(MainConsole.Instance.ConsoleScene);
+            int offsetX = 0;
+            int offsetY = 0;
+
+            int i = 0;
+            foreach (string param in cmd)
+            {
+                if (param.StartsWith("OffsetX"))
+                {
+                    string retVal = param.Remove(0, 8);
+                    int.TryParse(retVal, out offsetX);
+                }
+                else if (param.StartsWith("OffsetY"))
+                {
+                    string retVal = param.Remove(0, 8);
+                    int.TryParse(retVal, out offsetY);
+                }
+                i++;
             }

             foreach (TerrainModule tmodule in m)
@@ -1573,8 +1511,8 @@
             {
                 tmodule.SaveToFile((string)cmd[2]);
             }
-        }
-
+        }
+
         private void InterfaceSavePhysics(string module, string[] cmd)
         {
             List<TerrainModule> m = FindModuleForScene(MainConsole.Instance.ConsoleScene);
@@ -1619,28 +1557,28 @@
                 String direction = cmd[2];

                 if (direction.ToLower().StartsWith("y"))
-                {
+                {
                     for (int x = 0; x < m_scene.RegionInfo.RegionSizeX; x++)
-                    {
+                    {
                         for (int y = 0; y < m_scene.RegionInfo.RegionSizeY / 2; y++)
                         {
-                            double height = tmodule.m_channel[x, y];
+                            double height = tmodule.m_channel[x, y];
                             double flippedHeight = tmodule.m_channel[x, m_scene.RegionInfo.RegionSizeY - 1 - y];
-                            tmodule.m_channel[x, y] = flippedHeight;
+                            tmodule.m_channel[x, y] = flippedHeight;
                             tmodule.m_channel[x, m_scene.RegionInfo.RegionSizeY - 1 - y] = height;

                         }
                     }
                 }
                 else if (direction.ToLower().StartsWith("x"))
-                {
+                {
                     for (int y = 0; y < m_scene.RegionInfo.RegionSizeY; y++)
-                    {
+                    {
                         for (int x = 0; x < m_scene.RegionInfo.RegionSizeX / 2; x++)
                         {
-                            double height = tmodule.m_channel[x, y];
+                            double height = tmodule.m_channel[x, y];
                             double flippedHeight = tmodule.m_channel[m_scene.RegionInfo.RegionSizeX - 1 - x, y];
-                            tmodule.m_channel[x, y] = flippedHeight;
+                            tmodule.m_channel[x, y] = flippedHeight;
                             tmodule.m_channel[m_scene.RegionInfo.RegionSizeX - 1 - x, y] = height;

                         }
@@ -1902,14 +1840,14 @@
         {
             // Load / Save
             string supportedFileExtensions = "";
-            foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders)
-                supportedFileExtensions += " " + loader.Key + " (" + loader.Value + ")";
-
-            MainConsole.Instance.Commands.AddCommand("TerrainModule", true, "terrain save",
-                "terrain save <FileName>", "Saves the current heightmap to a specified file. FileName: The destination filename for your heightmap, the file extension determines the format to save in. Supported extensions include: " +
-                                          supportedFileExtensions, InterfaceSaveFile);
-
-            MainConsole.Instance.Commands.AddCommand("TerrainModule", true, "terrain physics update",
+            foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders)
+                supportedFileExtensions += " " + loader.Key + " (" + loader.Value + ")";
+
+            MainConsole.Instance.Commands.AddCommand("TerrainModule", true, "terrain save",
+                "terrain save <FileName>", "Saves the current heightmap to a specified file. FileName: The destination filename for your heightmap, the file extension determines the format to save in. Supported extensions include: " +
+                                          supportedFileExtensions, InterfaceSaveFile);
+
+            MainConsole.Instance.Commands.AddCommand("TerrainModule", true, "terrain physics update",
                 "terrain physics update", "Update the physics map", InterfaceSavePhysics);

             MainConsole.Instance.Commands.AddCommand("TerrainModule", true, "terrain load",


Overloaded IntPtr.LinkedListToEnum extension method to allow for offset func.

--- Core/Extensions/InteropExtensions.cs
+++ Core/Extensions/InteropExtensions.cs
@@ -214,6 +214,25 @@
 			}
 		}

+		/// <summary>Marshals an unmanaged linked list of structures to an <see cref="IEnumerable{T}"/> of that structure.</summary>
+		/// <typeparam name="T">Type of native structure used by the unmanaged linked list.</typeparam>
+		/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
+		/// <param name="nextOffset">The expression to be used to fetch the offset from the current pointer to the next item in the list.</param>
+		/// <param name="allocatedBytes">
+		/// The number of allocated bytes behind <paramref name="ptr"/>. This value is used to determine when to stop enumerating.
+		/// </param>
+		/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the linked list.</returns>
+		public static IEnumerable<T> LinkedListToIEnum<T>(this IntPtr ptr, Func<T, long> nextOffset, SizeT allocatedBytes)
+		{
+			var pEnd = ptr.Offset(allocatedBytes);
+			for (var pCurrent = ptr; pCurrent.ToInt64() < pEnd.ToInt64();)
+			{
+				var ret = pCurrent.ToStructure<T>();
+				yield return ret;
+				pCurrent = pCurrent.Offset(nextOffset(ret));
+			}
+		}
+
 		/// <summary>
 		/// Marshals data from a managed list of objects to an unmanaged block of memory allocated by the <paramref name="memAlloc"/> method.
 		/// </summary>


polished While

polished While