aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
diff options
context:
space:
mode:
authorRobert Adams2015-03-27 19:32:50 -0700
committerRobert Adams2015-03-27 19:32:50 -0700
commitbedafb8fae9898ef0c5fc6470236ee7244e616a9 (patch)
tree618728d92dad56dd587c243892d966b9ff9ea89b /OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
parentvarregion: restore checkAgentAccessToRegion routine in EntityTransferModule. (diff)
downloadopensim-SC_OLD-bedafb8fae9898ef0c5fc6470236ee7244e616a9.zip
opensim-SC_OLD-bedafb8fae9898ef0c5fc6470236ee7244e616a9.tar.gz
opensim-SC_OLD-bedafb8fae9898ef0c5fc6470236ee7244e616a9.tar.bz2
opensim-SC_OLD-bedafb8fae9898ef0c5fc6470236ee7244e616a9.tar.xz
varregion: refactor use of 'double heightmap[,]' into references to new class TerrainData
and push the implementation from Scene into the database readers and writers.
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs488
1 files changed, 390 insertions, 98 deletions
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
index 4d738a5..9a88804 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
@@ -71,6 +71,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
71 71
72 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 72 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
73 73
74#pragma warning disable 414
75 private static readonly string LogHeader = "[TERRAIN MODULE]";
76#pragma warning restore 414
77
74 private readonly Commander m_commander = new Commander("terrain"); 78 private readonly Commander m_commander = new Commander("terrain");
75 79
76 private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects = 80 private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects =
@@ -81,8 +85,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
81 private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects = 85 private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects =
82 new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>(); 86 new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>();
83 87
84 private ITerrainChannel m_channel;
85 private Dictionary<string, ITerrainEffect> m_plugineffects; 88 private Dictionary<string, ITerrainEffect> m_plugineffects;
89 private ITerrainChannel m_channel;
86 private ITerrainChannel m_revert; 90 private ITerrainChannel m_revert;
87 private Scene m_scene; 91 private Scene m_scene;
88 private volatile bool m_tainted; 92 private volatile bool m_tainted;
@@ -90,6 +94,85 @@ namespace OpenSim.Region.CoreModules.World.Terrain
90 94
91 private String m_InitialTerrain = "pinhead-island"; 95 private String m_InitialTerrain = "pinhead-island";
92 96
97 // If true, send terrain patch updates to clients based on their view distance
98 private bool m_sendTerrainUpdatesByViewDistance = true;
99
100 // Class to keep the per client collection of terrain patches that must be sent.
101 // A patch is set to 'true' meaning it should be sent to the client. Once the
102 // patch packet is queued to the client, the bit for that patch is set to 'false'.
103 private class PatchUpdates
104 {
105 private bool[,] updated; // for each patch, whether it needs to be sent to this client
106 private int updateCount; // number of patches that need to be sent
107 public ScenePresence Presence; // a reference to the client to send to
108 public PatchUpdates(TerrainData terrData, ScenePresence pPresence)
109 {
110 updated = new bool[terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize];
111 updateCount = 0;
112 Presence = pPresence;
113 // Initially, send all patches to the client
114 SetAll(true);
115 }
116 // Returns 'true' if there are any patches marked for sending
117 public bool HasUpdates()
118 {
119 return (updateCount > 0);
120 }
121 public void SetByXY(int x, int y, bool state)
122 {
123 this.SetByPatch(x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, state);
124 }
125 public bool GetByPatch(int patchX, int patchY)
126 {
127 return updated[patchX, patchY];
128 }
129 public void SetByPatch(int patchX, int patchY, bool state)
130 {
131 bool prevState = updated[patchX, patchY];
132 if (!prevState && state)
133 updateCount++;
134 if (prevState && !state)
135 updateCount--;
136 updated[patchX, patchY] = state;
137 }
138 public void SetAll(bool state)
139 {
140 updateCount = 0;
141 for (int xx = 0; xx < updated.GetLength(0); xx++)
142 for (int yy = 0; yy < updated.GetLength(1); yy++)
143 updated[xx, yy] = state;
144 if (state)
145 updateCount = updated.GetLength(0) * updated.GetLength(1);
146 }
147 // Logically OR's the terrain data's patch taint map into this client's update map.
148 public void SetAll(TerrainData terrData)
149 {
150 if (updated.GetLength(0) != (terrData.SizeX / Constants.TerrainPatchSize)
151 || updated.GetLength(1) != (terrData.SizeY / Constants.TerrainPatchSize))
152 {
153 throw new Exception(
154 String.Format("{0} PatchUpdates.SetAll: patch array not same size as terrain. arr=<{1},{2}>, terr=<{3},{4}>",
155 LogHeader, updated.GetLength(0), updated.GetLength(1),
156 terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize)
157 );
158 }
159 for (int xx = 0; xx < terrData.SizeX; xx += Constants.TerrainPatchSize)
160 {
161 for (int yy = 0; yy < terrData.SizeY; yy += Constants.TerrainPatchSize)
162 {
163 // Only set tainted. The patch bit may be set if the patch was to be sent later.
164 if (terrData.IsTaintedAt(xx, yy, false))
165 {
166 this.SetByXY(xx, yy, true);
167 }
168 }
169 }
170 }
171 }
172
173 // The flags of which terrain patches to send for each of the ScenePresence's
174 private Dictionary<UUID, PatchUpdates> m_perClientPatchUpdates = new Dictionary<UUID, PatchUpdates>();
175
93 /// <summary> 176 /// <summary>
94 /// Human readable list of terrain file extensions that are supported. 177 /// Human readable list of terrain file extensions that are supported.
95 /// </summary> 178 /// </summary>
@@ -118,7 +201,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
118 { 201 {
119 IConfig terrainConfig = config.Configs["Terrain"]; 202 IConfig terrainConfig = config.Configs["Terrain"];
120 if (terrainConfig != null) 203 if (terrainConfig != null)
204 {
121 m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); 205 m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain);
206 m_sendTerrainUpdatesByViewDistance = terrainConfig.GetBoolean("SendTerrainUpdatesByViewDistance", m_sendTerrainUpdatesByViewDistance);
207 }
122 } 208 }
123 209
124 public void AddRegion(Scene scene) 210 public void AddRegion(Scene scene)
@@ -130,22 +216,24 @@ namespace OpenSim.Region.CoreModules.World.Terrain
130 { 216 {
131 if (m_scene.Heightmap == null) 217 if (m_scene.Heightmap == null)
132 { 218 {
133 m_channel = new TerrainChannel(m_InitialTerrain); 219 m_channel = new TerrainChannel(m_InitialTerrain, (int)m_scene.RegionInfo.RegionSizeX,
220 (int)m_scene.RegionInfo.RegionSizeY,
221 (int)m_scene.RegionInfo.RegionSizeZ);
134 m_scene.Heightmap = m_channel; 222 m_scene.Heightmap = m_channel;
135 m_revert = new TerrainChannel();
136 UpdateRevertMap(); 223 UpdateRevertMap();
137 } 224 }
138 else 225 else
139 { 226 {
140 m_channel = m_scene.Heightmap; 227 m_channel = m_scene.Heightmap;
141 m_revert = new TerrainChannel();
142 UpdateRevertMap(); 228 UpdateRevertMap();
143 } 229 }
144 230
145 m_scene.RegisterModuleInterface<ITerrainModule>(this); 231 m_scene.RegisterModuleInterface<ITerrainModule>(this);
146 m_scene.EventManager.OnNewClient += EventManager_OnNewClient; 232 m_scene.EventManager.OnNewClient += EventManager_OnNewClient;
233 m_scene.EventManager.OnClientClosed += EventManager_OnClientClosed;
147 m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; 234 m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole;
148 m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick; 235 m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick;
236 m_scene.EventManager.OnFrame += EventManager_OnFrame;
149 } 237 }
150 238
151 InstallDefaultEffects(); 239 InstallDefaultEffects();
@@ -184,8 +272,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
184 // remove the commands 272 // remove the commands
185 m_scene.UnregisterModuleCommander(m_commander.Name); 273 m_scene.UnregisterModuleCommander(m_commander.Name);
186 // remove the event-handlers 274 // remove the event-handlers
275 m_scene.EventManager.OnFrame -= EventManager_OnFrame;
187 m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick; 276 m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick;
188 m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole; 277 m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole;
278 m_scene.EventManager.OnClientClosed -= EventManager_OnClientClosed;
189 m_scene.EventManager.OnNewClient -= EventManager_OnNewClient; 279 m_scene.EventManager.OnNewClient -= EventManager_OnNewClient;
190 // remove the interface 280 // remove the interface
191 m_scene.UnregisterModuleInterface<ITerrainModule>(this); 281 m_scene.UnregisterModuleInterface<ITerrainModule>(this);
@@ -230,11 +320,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
230 try 320 try
231 { 321 {
232 ITerrainChannel channel = loader.Value.LoadFile(filename); 322 ITerrainChannel channel = loader.Value.LoadFile(filename);
233 if (channel.Width != Constants.RegionSize || channel.Height != Constants.RegionSize) 323 if (channel.Width != m_scene.RegionInfo.RegionSizeX || channel.Height != m_scene.RegionInfo.RegionSizeY)
234 { 324 {
235 // TerrainChannel expects a RegionSize x RegionSize map, currently 325 // TerrainChannel expects a RegionSize x RegionSize map, currently
236 throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}", 326 throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}",
237 Constants.RegionSize, Constants.RegionSize)); 327 m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY));
238 } 328 }
239 m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height); 329 m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height);
240 m_scene.Heightmap = channel; 330 m_scene.Heightmap = channel;
@@ -261,7 +351,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
261 String.Format("Unable to load heightmap: {0}", e.Message)); 351 String.Format("Unable to load heightmap: {0}", e.Message));
262 } 352 }
263 } 353 }
264 CheckForTerrainUpdates();
265 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); 354 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
266 return; 355 return;
267 } 356 }
@@ -309,12 +398,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain
309 LoadFromStream(filename, URIFetch(pathToTerrainHeightmap)); 398 LoadFromStream(filename, URIFetch(pathToTerrainHeightmap));
310 } 399 }
311 400
401 public void LoadFromStream(string filename, Stream stream)
402 {
403 LoadFromStream(filename, Vector3.Zero, 0f, Vector2.Zero, stream);
404 }
405
312 /// <summary> 406 /// <summary>
313 /// Loads a terrain file from a stream and installs it in the scene. 407 /// Loads a terrain file from a stream and installs it in the scene.
314 /// </summary> 408 /// </summary>
315 /// <param name="filename">Filename to terrain file. Type is determined by extension.</param> 409 /// <param name="filename">Filename to terrain file. Type is determined by extension.</param>
316 /// <param name="stream"></param> 410 /// <param name="stream"></param>
317 public void LoadFromStream(string filename, Stream stream) 411 public void LoadFromStream(string filename, Vector3 displacement,
412 float radianRotation, Vector2 rotationDisplacement, Stream stream)
318 { 413 {
319 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) 414 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders)
320 { 415 {
@@ -325,8 +420,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
325 try 420 try
326 { 421 {
327 ITerrainChannel channel = loader.Value.LoadStream(stream); 422 ITerrainChannel channel = loader.Value.LoadStream(stream);
328 m_scene.Heightmap = channel; 423 m_channel.Merge(channel, displacement, radianRotation, rotationDisplacement);
329 m_channel = channel;
330 UpdateRevertMap(); 424 UpdateRevertMap();
331 } 425 }
332 catch (NotImplementedException) 426 catch (NotImplementedException)
@@ -337,7 +431,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
337 } 431 }
338 } 432 }
339 433
340 CheckForTerrainUpdates();
341 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); 434 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
342 return; 435 return;
343 } 436 }
@@ -406,9 +499,46 @@ namespace OpenSim.Region.CoreModules.World.Terrain
406 } 499 }
407 } 500 }
408 501
502 // Someone diddled terrain outside the normal code paths. Set the taintedness for all clients.
503 // ITerrainModule.TaintTerrain()
409 public void TaintTerrain () 504 public void TaintTerrain ()
410 { 505 {
411 CheckForTerrainUpdates(); 506 lock (m_perClientPatchUpdates)
507 {
508 // Set the flags for all clients so the tainted patches will be sent out
509 foreach (PatchUpdates pups in m_perClientPatchUpdates.Values)
510 {
511 pups.SetAll(m_scene.Heightmap.GetTerrainData());
512 }
513 }
514 }
515
516 // ITerrainModule.PushTerrain()
517 public void PushTerrain(IClientAPI pClient)
518 {
519 if (m_sendTerrainUpdatesByViewDistance)
520 {
521 ScenePresence presence = m_scene.GetScenePresence(pClient.AgentId);
522 if (presence != null)
523 {
524 lock (m_perClientPatchUpdates)
525 {
526 PatchUpdates pups;
527 if (!m_perClientPatchUpdates.TryGetValue(pClient.AgentId, out pups))
528 {
529 // There is a ScenePresence without a send patch map. Create one.
530 pups = new PatchUpdates(m_scene.Heightmap.GetTerrainData(), presence);
531 m_perClientPatchUpdates.Add(presence.UUID, pups);
532 }
533 pups.SetAll(true);
534 }
535 }
536 }
537 else
538 {
539 // The traditional way is to call into the protocol stack to send them all.
540 pClient.SendLayerData(new float[10]);
541 }
412 } 542 }
413 543
414 #region Plugin Loading Methods 544 #region Plugin Loading Methods
@@ -532,6 +662,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
532 /// </summary> 662 /// </summary>
533 public void UpdateRevertMap() 663 public void UpdateRevertMap()
534 { 664 {
665 /*
535 int x; 666 int x;
536 for (x = 0; x < m_channel.Width; x++) 667 for (x = 0; x < m_channel.Width; x++)
537 { 668 {
@@ -541,6 +672,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
541 m_revert[x, y] = m_channel[x, y]; 672 m_revert[x, y] = m_channel[x, y];
542 } 673 }
543 } 674 }
675 */
676 m_revert = m_channel.MakeCopy();
544 } 677 }
545 678
546 /// <summary> 679 /// <summary>
@@ -567,8 +700,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
567 { 700 {
568 ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY, 701 ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY,
569 fileWidth, fileHeight, 702 fileWidth, fileHeight,
570 (int) Constants.RegionSize, 703 (int) m_scene.RegionInfo.RegionSizeX,
571 (int) Constants.RegionSize); 704 (int) m_scene.RegionInfo.RegionSizeY);
572 m_scene.Heightmap = channel; 705 m_scene.Heightmap = channel;
573 m_channel = channel; 706 m_channel = channel;
574 UpdateRevertMap(); 707 UpdateRevertMap();
@@ -615,8 +748,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
615 { 748 {
616 loader.Value.SaveFile(m_channel, filename, offsetX, offsetY, 749 loader.Value.SaveFile(m_channel, filename, offsetX, offsetY,
617 fileWidth, fileHeight, 750 fileWidth, fileHeight,
618 (int)Constants.RegionSize, 751 (int)m_scene.RegionInfo.RegionSizeX,
619 (int)Constants.RegionSize); 752 (int)m_scene.RegionInfo.RegionSizeY);
620 753
621 MainConsole.Instance.OutputFormat( 754 MainConsole.Instance.OutputFormat(
622 "Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}", 755 "Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}",
@@ -634,7 +767,44 @@ namespace OpenSim.Region.CoreModules.World.Terrain
634 } 767 }
635 768
636 /// <summary> 769 /// <summary>
770 /// Called before processing of every simulation frame.
771 /// This is used to check to see of any of the terrain is tainted and, if so, schedule
772 /// updates for all the presences.
773 /// This also checks to see if there are updates that need to be sent for each presence.
774 /// This is where the logic is to send terrain updates to clients.
775 /// </summary>
776 private void EventManager_OnFrame()
777 {
778 TerrainData terrData = m_channel.GetTerrainData();
779
780 bool shouldTaint = false;
781 for (int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize)
782 {
783 for (int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize)
784 {
785 if (terrData.IsTaintedAt(x, y))
786 {
787 // Found a patch that was modified. Push this flag into the clients.
788 SendToClients(terrData, x, y);
789 shouldTaint = true;
790 }
791 }
792 }
793
794 // This event also causes changes to be sent to the clients
795 CheckSendingPatchesToClients();
796
797 // If things changes, generate some events
798 if (shouldTaint)
799 {
800 m_scene.EventManager.TriggerTerrainTainted();
801 m_tainted = true;
802 }
803 }
804
805 /// <summary>
637 /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections 806 /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections
807 /// Called infrequently (like every 5 seconds or so). Best used for storing terrain.
638 /// </summary> 808 /// </summary>
639 private void EventManager_OnTerrainTick() 809 private void EventManager_OnTerrainTick()
640 { 810 {
@@ -644,8 +814,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
644 m_scene.PhysicsScene.SetTerrain(m_channel.GetFloatsSerialised()); 814 m_scene.PhysicsScene.SetTerrain(m_channel.GetFloatsSerialised());
645 m_scene.SaveTerrain(); 815 m_scene.SaveTerrain();
646 816
647 m_scene.EventManager.TriggerTerrainUpdate();
648
649 // Clients who look at the map will never see changes after they looked at the map, so i've commented this out. 817 // Clients who look at the map will never see changes after they looked at the map, so i've commented this out.
650 //m_scene.CreateTerrainTexture(true); 818 //m_scene.CreateTerrainTexture(true);
651 } 819 }
@@ -687,54 +855,48 @@ namespace OpenSim.Region.CoreModules.World.Terrain
687 } 855 }
688 856
689 /// <summary> 857 /// <summary>
690 /// Checks to see if the terrain has been modified since last check 858 /// Installs terrain brush hook to IClientAPI
691 /// but won't attempt to limit those changes to the limits specified in the estate settings
692 /// currently invoked by the command line operations in the region server only
693 /// </summary> 859 /// </summary>
694 private void CheckForTerrainUpdates() 860 /// <param name="client"></param>
861 private void EventManager_OnClientClosed(UUID client, Scene scene)
695 { 862 {
696 CheckForTerrainUpdates(false); 863 ScenePresence presence = scene.GetScenePresence(client);
697 } 864 if (presence != null)
865 {
866 presence.ControllingClient.OnModifyTerrain -= client_OnModifyTerrain;
867 presence.ControllingClient.OnBakeTerrain -= client_OnBakeTerrain;
868 presence.ControllingClient.OnLandUndo -= client_OnLandUndo;
869 presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain;
870 }
698 871
872 lock (m_perClientPatchUpdates)
873 m_perClientPatchUpdates.Remove(client);
874 }
875
699 /// <summary> 876 /// <summary>
700 /// Checks to see if the terrain has been modified since last check. 877 /// Scan over changes in the terrain and limit height changes. This enforces the
701 /// If it has been modified, every all the terrain patches are sent to the client. 878 /// non-estate owner limits on rate of terrain editting.
702 /// If the call is asked to respect the estate settings for terrain_raise_limit and 879 /// Returns 'true' if any heights were limited.
703 /// terrain_lower_limit, it will clamp terrain updates between these values
704 /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces
705 /// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param>
706 /// </summary> 880 /// </summary>
707 private void CheckForTerrainUpdates(bool respectEstateSettings) 881 private bool EnforceEstateLimits()
708 { 882 {
709 bool shouldTaint = false; 883 TerrainData terrData = m_channel.GetTerrainData();
710 float[] serialised = m_channel.GetFloatsSerialised(); 884
711 int x; 885 bool wasLimited = false;
712 for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize) 886 for (int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize)
713 { 887 {
714 int y; 888 for (int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize)
715 for (y = 0; y < m_channel.Height; y += Constants.TerrainPatchSize)
716 { 889 {
717 if (m_channel.Tainted(x, y)) 890 if (terrData.IsTaintedAt(x, y, false /* clearOnTest */))
718 { 891 {
719 // if we should respect the estate settings then 892 // If we should respect the estate settings then
720 // fixup and height deltas that don't respect them 893 // fixup and height deltas that don't respect them.
721 if (respectEstateSettings && LimitChannelChanges(x, y)) 894 // Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values.
722 { 895 wasLimited |= LimitChannelChanges(terrData, x, y);
723 // this has been vetoed, so update
724 // what we are going to send to the client
725 serialised = m_channel.GetFloatsSerialised();
726 }
727
728 SendToClients(serialised, x, y);
729 shouldTaint = true;
730 } 896 }
731 } 897 }
732 } 898 }
733 if (shouldTaint) 899 return wasLimited;
734 {
735 m_scene.EventManager.TriggerTerrainTainted();
736 m_tainted = true;
737 }
738 } 900 }
739 901
740 /// <summary> 902 /// <summary>
@@ -742,11 +904,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
742 /// are all within the current estate limits 904 /// are all within the current estate limits
743 /// <returns>true if changes were limited, false otherwise</returns> 905 /// <returns>true if changes were limited, false otherwise</returns>
744 /// </summary> 906 /// </summary>
745 private bool LimitChannelChanges(int xStart, int yStart) 907 private bool LimitChannelChanges(TerrainData terrData, int xStart, int yStart)
746 { 908 {
747 bool changesLimited = false; 909 bool changesLimited = false;
748 double minDelta = m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; 910 float minDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit;
749 double maxDelta = m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; 911 float maxDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit;
750 912
751 // loop through the height map for this patch and compare it against 913 // loop through the height map for this patch and compare it against
752 // the revert map 914 // the revert map
@@ -754,19 +916,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain
754 { 916 {
755 for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) 917 for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++)
756 { 918 {
757 919 float requestedHeight = terrData[x, y];
758 double requestedHeight = m_channel[x, y]; 920 float bakedHeight = (float)m_revert[x, y];
759 double bakedHeight = m_revert[x, y]; 921 float requestedDelta = requestedHeight - bakedHeight;
760 double requestedDelta = requestedHeight - bakedHeight;
761 922
762 if (requestedDelta > maxDelta) 923 if (requestedDelta > maxDelta)
763 { 924 {
764 m_channel[x, y] = bakedHeight + maxDelta; 925 terrData[x, y] = bakedHeight + maxDelta;
765 changesLimited = true; 926 changesLimited = true;
766 } 927 }
767 else if (requestedDelta < minDelta) 928 else if (requestedDelta < minDelta)
768 { 929 {
769 m_channel[x, y] = bakedHeight + minDelta; //as lower is a -ve delta 930 terrData[x, y] = bakedHeight + minDelta; //as lower is a -ve delta
770 changesLimited = true; 931 changesLimited = true;
771 } 932 }
772 } 933 }
@@ -794,14 +955,154 @@ namespace OpenSim.Region.CoreModules.World.Terrain
794 /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param> 955 /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param>
795 /// <param name="x">The patch corner to send</param> 956 /// <param name="x">The patch corner to send</param>
796 /// <param name="y">The patch corner to send</param> 957 /// <param name="y">The patch corner to send</param>
797 private void SendToClients(float[] serialised, int x, int y) 958 private void SendToClients(TerrainData terrData, int x, int y)
959 {
960 if (m_sendTerrainUpdatesByViewDistance)
961 {
962 // Add that this patch needs to be sent to the accounting for each client.
963 lock (m_perClientPatchUpdates)
964 {
965 m_scene.ForEachScenePresence(presence =>
966 {
967 PatchUpdates thisClientUpdates;
968 if (!m_perClientPatchUpdates.TryGetValue(presence.UUID, out thisClientUpdates))
969 {
970 // There is a ScenePresence without a send patch map. Create one.
971 thisClientUpdates = new PatchUpdates(terrData, presence);
972 m_perClientPatchUpdates.Add(presence.UUID, thisClientUpdates);
973 }
974 thisClientUpdates.SetByXY(x, y, true);
975 }
976 );
977 }
978 }
979 else
980 {
981 // Legacy update sending where the update is sent out as soon as noticed
982 // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI.
983 //float[] heightMap = terrData.GetFloatsSerialized();
984 float[] heightMap = new float[10];
985 m_scene.ForEachClient(
986 delegate(IClientAPI controller)
987 {
988 controller.SendLayerData(x / Constants.TerrainPatchSize,
989 y / Constants.TerrainPatchSize,
990 heightMap);
991 }
992 );
993 }
994 }
995
996 private class PatchesToSend : IComparable<PatchesToSend>
798 { 997 {
799 m_scene.ForEachClient( 998 public int PatchX;
800 delegate(IClientAPI controller) 999 public int PatchY;
801 { controller.SendLayerData( 1000 public float Dist;
802 x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, serialised); 1001 public PatchesToSend(int pX, int pY, float pDist)
1002 {
1003 PatchX = pX;
1004 PatchY = pY;
1005 Dist = pDist;
1006 }
1007 public int CompareTo(PatchesToSend other)
1008 {
1009 return Dist.CompareTo(other.Dist);
1010 }
1011 }
1012
1013 // Called each frame time to see if there are any patches to send to any of the
1014 // ScenePresences.
1015 // Loop through all the per-client info and send any patches necessary.
1016 private void CheckSendingPatchesToClients()
1017 {
1018 lock (m_perClientPatchUpdates)
1019 {
1020 foreach (PatchUpdates pups in m_perClientPatchUpdates.Values)
1021 {
1022 if (pups.HasUpdates())
1023 {
1024 // There is something that could be sent to this client.
1025 List<PatchesToSend> toSend = GetModifiedPatchesInViewDistance(pups);
1026 if (toSend.Count > 0)
1027 {
1028 // m_log.DebugFormat("{0} CheckSendingPatchesToClient: sending {1} patches to {2} in region {3}",
1029 // LogHeader, toSend.Count, pups.Presence.Name, m_scene.RegionInfo.RegionName);
1030 // Sort the patches to send by the distance from the presence
1031 toSend.Sort();
1032 /*
1033 foreach (PatchesToSend pts in toSend)
1034 {
1035 pups.Presence.ControllingClient.SendLayerData(pts.PatchX, pts.PatchY, null);
1036 // presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), null, TerrainPatch.LayerType.Land);
1037 }
1038 */
1039
1040 int[] xPieces = new int[toSend.Count];
1041 int[] yPieces = new int[toSend.Count];
1042 float[] patchPieces = new float[toSend.Count * 2];
1043 int pieceIndex = 0;
1044 foreach (PatchesToSend pts in toSend)
1045 {
1046 patchPieces[pieceIndex++] = pts.PatchX;
1047 patchPieces[pieceIndex++] = pts.PatchY;
1048 }
1049 pups.Presence.ControllingClient.SendLayerData(-toSend.Count, 0, patchPieces);
1050 }
1051 }
1052 }
1053 }
1054 }
1055
1056 private List<PatchesToSend> GetModifiedPatchesInViewDistance(PatchUpdates pups)
1057 {
1058 List<PatchesToSend> ret = new List<PatchesToSend>();
1059
1060 ScenePresence presence = pups.Presence;
1061 if (presence == null)
1062 return ret;
1063
1064 // Compute the area of patches within our draw distance
1065 int startX = (((int) (presence.AbsolutePosition.X - presence.DrawDistance))/Constants.TerrainPatchSize) - 2;
1066 startX = Math.Max(startX, 0);
1067 startX = Math.Min(startX, (int)m_scene.RegionInfo.RegionSizeX/Constants.TerrainPatchSize);
1068 int startY = (((int) (presence.AbsolutePosition.Y - presence.DrawDistance))/Constants.TerrainPatchSize) - 2;
1069 startY = Math.Max(startY, 0);
1070 startY = Math.Min(startY, (int)m_scene.RegionInfo.RegionSizeY/Constants.TerrainPatchSize);
1071 int endX = (((int) (presence.AbsolutePosition.X + presence.DrawDistance))/Constants.TerrainPatchSize) + 2;
1072 endX = Math.Max(endX, 0);
1073 endX = Math.Min(endX, (int)m_scene.RegionInfo.RegionSizeX/Constants.TerrainPatchSize);
1074 int endY = (((int) (presence.AbsolutePosition.Y + presence.DrawDistance))/Constants.TerrainPatchSize) + 2;
1075 endY = Math.Max(endY, 0);
1076 endY = Math.Min(endY, (int)m_scene.RegionInfo.RegionSizeY/Constants.TerrainPatchSize);
1077 // m_log.DebugFormat("{0} GetModifiedPatchesInViewDistance. rName={1}, ddist={2}, apos={3}, start=<{4},{5}>, end=<{6},{7}>",
1078 // LogHeader, m_scene.RegionInfo.RegionName,
1079 // presence.DrawDistance, presence.AbsolutePosition,
1080 // startX, startY, endX, endY);
1081 for (int x = startX; x < endX; x++)
1082 {
1083 for (int y = startY; y < endY; y++)
1084 {
1085 //Need to make sure we don't send the same ones over and over
1086 Vector3 presencePos = presence.AbsolutePosition;
1087 Vector3 patchPos = new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, presencePos.Z);
1088 if (pups.GetByPatch(x, y))
1089 {
1090 //Check which has less distance, camera or avatar position, both have to be done.
1091 //Its not a radius, its a diameter and we add 50 so that it doesn't look like it cuts off
1092 if (Util.DistanceLessThan(presencePos, patchPos, presence.DrawDistance + 50)
1093 || Util.DistanceLessThan(presence.CameraPosition, patchPos, presence.DrawDistance + 50))
1094 {
1095 //They can see it, send it to them
1096 pups.SetByPatch(x, y, false);
1097 float dist = Vector3.DistanceSquared(presencePos, patchPos);
1098 ret.Add(new PatchesToSend(x, y, dist));
1099 //Wait and send them all at once
1100 // pups.client.SendLayerData(x, y, null);
1101 }
803 } 1102 }
804 ); 1103 }
1104 }
1105 return ret;
805 } 1106 }
806 1107
807 private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, 1108 private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action,
@@ -846,7 +1147,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain
846 m_painteffects[(StandardTerrainEffects) action].PaintEffect( 1147 m_painteffects[(StandardTerrainEffects) action].PaintEffect(
847 m_channel, allowMask, west, south, height, size, seconds); 1148 m_channel, allowMask, west, south, height, size, seconds);
848 1149
849 CheckForTerrainUpdates(!god); //revert changes outside estate limits 1150 //revert changes outside estate limits
1151 if (!god)
1152 EnforceEstateLimits();
850 } 1153 }
851 } 1154 }
852 else 1155 else
@@ -884,10 +1187,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
884 if (allowed) 1187 if (allowed)
885 { 1188 {
886 StoreUndoState(); 1189 StoreUndoState();
887 m_floodeffects[(StandardTerrainEffects) action].FloodEffect( 1190 m_floodeffects[(StandardTerrainEffects) action].FloodEffect(m_channel, fillArea, size);
888 m_channel, fillArea, size);
889 1191
890 CheckForTerrainUpdates(!god); //revert changes outside estate limits 1192 //revert changes outside estate limits
1193 if (!god)
1194 EnforceEstateLimits();
891 } 1195 }
892 } 1196 }
893 else 1197 else
@@ -911,7 +1215,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain
911 protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY) 1215 protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY)
912 { 1216 {
913 //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY); 1217 //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY);
914 client.SendLayerData(patchX, patchY, m_scene.Heightmap.GetFloatsSerialised()); 1218 // SendLayerData does not use the heightmap parameter. This kludge is so as to not change IClientAPI.
1219 float[] heightMap = new float[10];
1220 client.SendLayerData(patchX, patchY, heightMap);
915 } 1221 }
916 1222
917 private void StoreUndoState() 1223 private void StoreUndoState()
@@ -938,7 +1244,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
938 private void InterfaceLoadFile(Object[] args) 1244 private void InterfaceLoadFile(Object[] args)
939 { 1245 {
940 LoadFromFile((string) args[0]); 1246 LoadFromFile((string) args[0]);
941 CheckForTerrainUpdates();
942 } 1247 }
943 1248
944 private void InterfaceLoadTileFile(Object[] args) 1249 private void InterfaceLoadTileFile(Object[] args)
@@ -948,7 +1253,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
948 (int) args[2], 1253 (int) args[2],
949 (int) args[3], 1254 (int) args[3],
950 (int) args[4]); 1255 (int) args[4]);
951 CheckForTerrainUpdates();
952 } 1256 }
953 1257
954 private void InterfaceSaveFile(Object[] args) 1258 private void InterfaceSaveFile(Object[] args)
@@ -977,7 +1281,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
977 for (y = 0; y < m_channel.Height; y++) 1281 for (y = 0; y < m_channel.Height; y++)
978 m_channel[x, y] = m_revert[x, y]; 1282 m_channel[x, y] = m_revert[x, y];
979 1283
980 CheckForTerrainUpdates();
981 } 1284 }
982 1285
983 private void InterfaceFlipTerrain(Object[] args) 1286 private void InterfaceFlipTerrain(Object[] args)
@@ -986,28 +1289,28 @@ namespace OpenSim.Region.CoreModules.World.Terrain
986 1289
987 if (direction.ToLower().StartsWith("y")) 1290 if (direction.ToLower().StartsWith("y"))
988 { 1291 {
989 for (int x = 0; x < Constants.RegionSize; x++) 1292 for (int x = 0; x < m_channel.Width; x++)
990 { 1293 {
991 for (int y = 0; y < Constants.RegionSize / 2; y++) 1294 for (int y = 0; y < m_channel.Height / 2; y++)
992 { 1295 {
993 double height = m_channel[x, y]; 1296 double height = m_channel[x, y];
994 double flippedHeight = m_channel[x, (int)Constants.RegionSize - 1 - y]; 1297 double flippedHeight = m_channel[x, (int)m_channel.Height - 1 - y];
995 m_channel[x, y] = flippedHeight; 1298 m_channel[x, y] = flippedHeight;
996 m_channel[x, (int)Constants.RegionSize - 1 - y] = height; 1299 m_channel[x, (int)m_channel.Height - 1 - y] = height;
997 1300
998 } 1301 }
999 } 1302 }
1000 } 1303 }
1001 else if (direction.ToLower().StartsWith("x")) 1304 else if (direction.ToLower().StartsWith("x"))
1002 { 1305 {
1003 for (int y = 0; y < Constants.RegionSize; y++) 1306 for (int y = 0; y < m_channel.Height; y++)
1004 { 1307 {
1005 for (int x = 0; x < Constants.RegionSize / 2; x++) 1308 for (int x = 0; x < m_channel.Width / 2; x++)
1006 { 1309 {
1007 double height = m_channel[x, y]; 1310 double height = m_channel[x, y];
1008 double flippedHeight = m_channel[(int)Constants.RegionSize - 1 - x, y]; 1311 double flippedHeight = m_channel[(int)m_channel.Width - 1 - x, y];
1009 m_channel[x, y] = flippedHeight; 1312 m_channel[x, y] = flippedHeight;
1010 m_channel[(int)Constants.RegionSize - 1 - x, y] = height; 1313 m_channel[(int)m_channel.Width - 1 - x, y] = height;
1011 1314
1012 } 1315 }
1013 } 1316 }
@@ -1016,9 +1319,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1016 { 1319 {
1017 m_log.Error("Unrecognised direction - need x or y"); 1320 m_log.Error("Unrecognised direction - need x or y");
1018 } 1321 }
1019
1020
1021 CheckForTerrainUpdates();
1022 } 1322 }
1023 1323
1024 private void InterfaceRescaleTerrain(Object[] args) 1324 private void InterfaceRescaleTerrain(Object[] args)
@@ -1076,7 +1376,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1076 } 1376 }
1077 } 1377 }
1078 1378
1079 CheckForTerrainUpdates();
1080 } 1379 }
1081 1380
1082 } 1381 }
@@ -1087,7 +1386,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1087 for (x = 0; x < m_channel.Width; x++) 1386 for (x = 0; x < m_channel.Width; x++)
1088 for (y = 0; y < m_channel.Height; y++) 1387 for (y = 0; y < m_channel.Height; y++)
1089 m_channel[x, y] += (double) args[0]; 1388 m_channel[x, y] += (double) args[0];
1090 CheckForTerrainUpdates();
1091 } 1389 }
1092 1390
1093 private void InterfaceMultiplyTerrain(Object[] args) 1391 private void InterfaceMultiplyTerrain(Object[] args)
@@ -1096,7 +1394,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1096 for (x = 0; x < m_channel.Width; x++) 1394 for (x = 0; x < m_channel.Width; x++)
1097 for (y = 0; y < m_channel.Height; y++) 1395 for (y = 0; y < m_channel.Height; y++)
1098 m_channel[x, y] *= (double) args[0]; 1396 m_channel[x, y] *= (double) args[0];
1099 CheckForTerrainUpdates();
1100 } 1397 }
1101 1398
1102 private void InterfaceLowerTerrain(Object[] args) 1399 private void InterfaceLowerTerrain(Object[] args)
@@ -1105,17 +1402,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1105 for (x = 0; x < m_channel.Width; x++) 1402 for (x = 0; x < m_channel.Width; x++)
1106 for (y = 0; y < m_channel.Height; y++) 1403 for (y = 0; y < m_channel.Height; y++)
1107 m_channel[x, y] -= (double) args[0]; 1404 m_channel[x, y] -= (double) args[0];
1108 CheckForTerrainUpdates();
1109 } 1405 }
1110 1406
1111 private void InterfaceFillTerrain(Object[] args) 1407 public void InterfaceFillTerrain(Object[] args)
1112 { 1408 {
1113 int x, y; 1409 int x, y;
1114 1410
1115 for (x = 0; x < m_channel.Width; x++) 1411 for (x = 0; x < m_channel.Width; x++)
1116 for (y = 0; y < m_channel.Height; y++) 1412 for (y = 0; y < m_channel.Height; y++)
1117 m_channel[x, y] = (double) args[0]; 1413 m_channel[x, y] = (double) args[0];
1118 CheckForTerrainUpdates();
1119 } 1414 }
1120 1415
1121 private void InterfaceMinTerrain(Object[] args) 1416 private void InterfaceMinTerrain(Object[] args)
@@ -1128,7 +1423,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1128 m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]); 1423 m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]);
1129 } 1424 }
1130 } 1425 }
1131 CheckForTerrainUpdates();
1132 } 1426 }
1133 1427
1134 private void InterfaceMaxTerrain(Object[] args) 1428 private void InterfaceMaxTerrain(Object[] args)
@@ -1141,7 +1435,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1141 m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]); 1435 m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]);
1142 } 1436 }
1143 } 1437 }
1144 CheckForTerrainUpdates();
1145 } 1438 }
1146 1439
1147 private void InterfaceShowDebugStats(Object[] args) 1440 private void InterfaceShowDebugStats(Object[] args)
@@ -1204,7 +1497,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1204 if (m_plugineffects.ContainsKey(firstArg)) 1497 if (m_plugineffects.ContainsKey(firstArg))
1205 { 1498 {
1206 m_plugineffects[firstArg].RunEffect(m_channel); 1499 m_plugineffects[firstArg].RunEffect(m_channel);
1207 CheckForTerrainUpdates();
1208 } 1500 }
1209 else 1501 else
1210 { 1502 {