aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
diff options
context:
space:
mode:
authorDavid Walter Seikel2016-11-03 21:44:39 +1000
committerDavid Walter Seikel2016-11-03 21:44:39 +1000
commit134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch)
tree216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
parentMore changing to production grid. Double oops. (diff)
downloadopensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip
opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz
opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2
opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.xz
Initial update to OpenSim 0.8.2.1 source code.
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs')
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs814
1 files changed, 613 insertions, 201 deletions
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
index fd30c46..932652c 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
@@ -24,19 +24,24 @@
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27
28using System; 27using System;
29using System.Collections.Generic; 28using System.Collections.Generic;
30using System.IO; 29using System.IO;
31using System.Reflection; 30using System.Reflection;
32using System.Net; 31using System.Net;
32
33using log4net; 33using log4net;
34using Nini.Config; 34using Nini.Config;
35
35using OpenMetaverse; 36using OpenMetaverse;
36using Mono.Addins; 37using Mono.Addins;
38
39using OpenSim.Data;
37using OpenSim.Framework; 40using OpenSim.Framework;
41using OpenSim.Framework.Console;
38using OpenSim.Region.CoreModules.Framework.InterfaceCommander; 42using OpenSim.Region.CoreModules.Framework.InterfaceCommander;
39using OpenSim.Region.CoreModules.World.Terrain.FileLoaders; 43using OpenSim.Region.CoreModules.World.Terrain.FileLoaders;
44using OpenSim.Region.CoreModules.World.Terrain.Modifiers;
40using OpenSim.Region.CoreModules.World.Terrain.FloodBrushes; 45using OpenSim.Region.CoreModules.World.Terrain.FloodBrushes;
41using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes; 46using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes;
42using OpenSim.Region.Framework.Interfaces; 47using OpenSim.Region.Framework.Interfaces;
@@ -70,26 +75,112 @@ namespace OpenSim.Region.CoreModules.World.Terrain
70 #endregion 75 #endregion
71 76
72 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 77 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
73
74 private readonly Commander m_commander = new Commander("terrain");
75 78
79#pragma warning disable 414
80 private static readonly string LogHeader = "[TERRAIN MODULE]";
81#pragma warning restore 414
82
83 private readonly Commander m_commander = new Commander("terrain");
76 private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects = 84 private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects =
77 new Dictionary<StandardTerrainEffects, ITerrainFloodEffect>(); 85 new Dictionary<StandardTerrainEffects, ITerrainFloodEffect>();
78
79 private readonly Dictionary<string, ITerrainLoader> m_loaders = new Dictionary<string, ITerrainLoader>(); 86 private readonly Dictionary<string, ITerrainLoader> m_loaders = new Dictionary<string, ITerrainLoader>();
80
81 private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects = 87 private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects =
82 new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>(); 88 new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>();
83
84 private ITerrainChannel m_channel;
85 private Dictionary<string, ITerrainEffect> m_plugineffects; 89 private Dictionary<string, ITerrainEffect> m_plugineffects;
90 private Dictionary<string, ITerrainModifier> m_modifyOperations =
91 new Dictionary<string, ITerrainModifier>();
92 private ITerrainChannel m_channel;
86 private ITerrainChannel m_revert; 93 private ITerrainChannel m_revert;
87 private Scene m_scene; 94 private Scene m_scene;
88 private volatile bool m_tainted; 95 private volatile bool m_tainted;
89 private readonly Stack<LandUndoState> m_undo = new Stack<LandUndoState>(5); 96 private readonly Stack<LandUndoState> m_undo = new Stack<LandUndoState>(5);
90
91 private String m_InitialTerrain = "pinhead-island"; 97 private String m_InitialTerrain = "pinhead-island";
92 98
99 // If true, send terrain patch updates to clients based on their view distance
100 private bool m_sendTerrainUpdatesByViewDistance = true;
101
102 // Class to keep the per client collection of terrain patches that must be sent.
103 // A patch is set to 'true' meaning it should be sent to the client. Once the
104 // patch packet is queued to the client, the bit for that patch is set to 'false'.
105 private class PatchUpdates
106 {
107 private bool[,] updated; // for each patch, whether it needs to be sent to this client
108 private int updateCount; // number of patches that need to be sent
109 public ScenePresence Presence; // a reference to the client to send to
110 public TerrainData Terrain; // reference to the underlying terrain
111 public PatchUpdates(TerrainData terrData, ScenePresence pPresence)
112 {
113 updated = new bool[terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize];
114 updateCount = 0;
115 Presence = pPresence;
116 Terrain = terrData;
117 // Initially, send all patches to the client
118 SetAll(true);
119 }
120 // Returns 'true' if there are any patches marked for sending
121 public bool HasUpdates()
122 {
123 return (updateCount > 0);
124 }
125
126 public void SetByXY(int x, int y, bool state)
127 {
128 this.SetByPatch(x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, state);
129 }
130
131 public bool GetByPatch(int patchX, int patchY)
132 {
133 return updated[patchX, patchY];
134 }
135
136 public void SetByPatch(int patchX, int patchY, bool state)
137 {
138 bool prevState = updated[patchX, patchY];
139 if (!prevState && state)
140 updateCount++;
141 if (prevState && !state)
142 updateCount--;
143 updated[patchX, patchY] = state;
144 }
145
146 public void SetAll(bool state)
147 {
148 updateCount = 0;
149 for(int xx = 0; xx < updated.GetLength(0); xx++)
150 for(int yy = 0; yy < updated.GetLength(1); yy++)
151 updated[xx, yy] = state;
152 if (state)
153 updateCount = updated.GetLength(0) * updated.GetLength(1);
154 }
155 // Logically OR's the terrain data's patch taint map into this client's update map.
156 public void SetAll(TerrainData terrData)
157 {
158 if (updated.GetLength(0) != (terrData.SizeX / Constants.TerrainPatchSize)
159 || updated.GetLength(1) != (terrData.SizeY / Constants.TerrainPatchSize))
160 {
161 throw new Exception(
162 String.Format("{0} PatchUpdates.SetAll: patch array not same size as terrain. arr=<{1},{2}>, terr=<{3},{4}>",
163 LogHeader, updated.GetLength(0), updated.GetLength(1),
164 terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize)
165 );
166 }
167 for(int xx = 0; xx < terrData.SizeX; xx += Constants.TerrainPatchSize)
168 {
169 for(int yy = 0; yy < terrData.SizeY; yy += Constants.TerrainPatchSize)
170 {
171 // Only set tainted. The patch bit may be set if the patch was to be sent later.
172 if (terrData.IsTaintedAt(xx, yy, false))
173 {
174 this.SetByXY(xx, yy, true);
175 }
176 }
177 }
178 }
179 }
180
181 // The flags of which terrain patches to send for each of the ScenePresence's
182 private Dictionary<UUID, PatchUpdates> m_perClientPatchUpdates = new Dictionary<UUID, PatchUpdates>();
183
93 /// <summary> 184 /// <summary>
94 /// Human readable list of terrain file extensions that are supported. 185 /// Human readable list of terrain file extensions that are supported.
95 /// </summary> 186 /// </summary>
@@ -100,8 +191,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
100 191
101 #region ICommandableModule Members 192 #region ICommandableModule Members
102 193
103 public ICommander CommandInterface 194 public ICommander CommandInterface {
104 {
105 get { return m_commander; } 195 get { return m_commander; }
106 } 196 }
107 197
@@ -118,7 +208,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
118 { 208 {
119 IConfig terrainConfig = config.Configs["Terrain"]; 209 IConfig terrainConfig = config.Configs["Terrain"];
120 if (terrainConfig != null) 210 if (terrainConfig != null)
211 {
121 m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); 212 m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain);
213 m_sendTerrainUpdatesByViewDistance = terrainConfig.GetBoolean("SendTerrainUpdatesByViewDistance", m_sendTerrainUpdatesByViewDistance);
214 }
122 } 215 }
123 216
124 public void AddRegion(Scene scene) 217 public void AddRegion(Scene scene)
@@ -126,26 +219,28 @@ namespace OpenSim.Region.CoreModules.World.Terrain
126 m_scene = scene; 219 m_scene = scene;
127 220
128 // Install terrain module in the simulator 221 // Install terrain module in the simulator
129 lock (m_scene) 222 lock(m_scene)
130 { 223 {
131 if (m_scene.Heightmap == null) 224 if (m_scene.Heightmap == null)
132 { 225 {
133 m_channel = new TerrainChannel(m_InitialTerrain); 226 m_channel = new TerrainChannel(m_InitialTerrain, (int)m_scene.RegionInfo.RegionSizeX,
227 (int)m_scene.RegionInfo.RegionSizeY,
228 (int)m_scene.RegionInfo.RegionSizeZ);
134 m_scene.Heightmap = m_channel; 229 m_scene.Heightmap = m_channel;
135 m_revert = new TerrainChannel();
136 UpdateRevertMap(); 230 UpdateRevertMap();
137 } 231 }
138 else 232 else
139 { 233 {
140 m_channel = m_scene.Heightmap; 234 m_channel = m_scene.Heightmap;
141 m_revert = new TerrainChannel();
142 UpdateRevertMap(); 235 UpdateRevertMap();
143 } 236 }
144 237
145 m_scene.RegisterModuleInterface<ITerrainModule>(this); 238 m_scene.RegisterModuleInterface<ITerrainModule>(this);
146 m_scene.EventManager.OnNewClient += EventManager_OnNewClient; 239 m_scene.EventManager.OnNewClient += EventManager_OnNewClient;
240 m_scene.EventManager.OnClientClosed += EventManager_OnClientClosed;
147 m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; 241 m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole;
148 m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick; 242 m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick;
243 m_scene.EventManager.OnFrame += EventManager_OnFrame;
149 } 244 }
150 245
151 InstallDefaultEffects(); 246 InstallDefaultEffects();
@@ -156,7 +251,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
156 string supportedFilesSeparatorForTileSave = ""; 251 string supportedFilesSeparatorForTileSave = "";
157 252
158 m_supportFileExtensionsForTileSave = ""; 253 m_supportFileExtensionsForTileSave = "";
159 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) 254 foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders)
160 { 255 {
161 m_supportedFileExtensions += supportedFilesSeparator + loader.Key + " (" + loader.Value + ")"; 256 m_supportedFileExtensions += supportedFilesSeparator + loader.Key + " (" + loader.Value + ")";
162 supportedFilesSeparator = ", "; 257 supportedFilesSeparator = ", ";
@@ -179,13 +274,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain
179 274
180 public void RemoveRegion(Scene scene) 275 public void RemoveRegion(Scene scene)
181 { 276 {
182 lock (m_scene) 277 lock(m_scene)
183 { 278 {
184 // remove the commands 279 // remove the commands
185 m_scene.UnregisterModuleCommander(m_commander.Name); 280 m_scene.UnregisterModuleCommander(m_commander.Name);
186 // remove the event-handlers 281 // remove the event-handlers
282 m_scene.EventManager.OnFrame -= EventManager_OnFrame;
187 m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick; 283 m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick;
188 m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole; 284 m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole;
285 m_scene.EventManager.OnClientClosed -= EventManager_OnClientClosed;
189 m_scene.EventManager.OnNewClient -= EventManager_OnNewClient; 286 m_scene.EventManager.OnNewClient -= EventManager_OnNewClient;
190 // remove the interface 287 // remove the interface
191 m_scene.UnregisterModuleInterface<ITerrainModule>(this); 288 m_scene.UnregisterModuleInterface<ITerrainModule>(this);
@@ -196,13 +293,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
196 { 293 {
197 } 294 }
198 295
199 public Type ReplaceableInterface 296 public Type ReplaceableInterface {
200 {
201 get { return null; } 297 get { return null; }
202 } 298 }
203 299
204 public string Name 300 public string Name {
205 {
206 get { return "TerrainModule"; } 301 get { return "TerrainModule"; }
207 } 302 }
208 303
@@ -221,47 +316,46 @@ namespace OpenSim.Region.CoreModules.World.Terrain
221 /// <param name="filename">Filename to terrain file. Type is determined by extension.</param> 316 /// <param name="filename">Filename to terrain file. Type is determined by extension.</param>
222 public void LoadFromFile(string filename) 317 public void LoadFromFile(string filename)
223 { 318 {
224 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) 319 foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders)
225 { 320 {
226 if (filename.EndsWith(loader.Key)) 321 if (filename.EndsWith(loader.Key))
227 { 322 {
228 lock (m_scene) 323 lock(m_scene)
229 { 324 {
230 try 325 try
231 { 326 {
232 ITerrainChannel channel = loader.Value.LoadFile(filename); 327 ITerrainChannel channel = loader.Value.LoadFile(filename);
233 if (channel.Width != Constants.RegionSize || channel.Height != Constants.RegionSize) 328 if (channel.Width != m_scene.RegionInfo.RegionSizeX || channel.Height != m_scene.RegionInfo.RegionSizeY)
234 { 329 {
235 // TerrainChannel expects a RegionSize x RegionSize map, currently 330 // TerrainChannel expects a RegionSize x RegionSize map, currently
236 throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}", 331 throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}",
237 Constants.RegionSize, Constants.RegionSize)); 332 m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY));
238 } 333 }
239 m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height); 334 m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height);
240 m_scene.Heightmap = channel; 335 m_scene.Heightmap = channel;
241 m_channel = channel; 336 m_channel = channel;
242 UpdateRevertMap(); 337 UpdateRevertMap();
243 } 338 }
244 catch (NotImplementedException) 339 catch(NotImplementedException)
245 { 340 {
246 m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value + 341 m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value +
247 " parser does not support file loading. (May be save only)"); 342 " parser does not support file loading. (May be save only)");
248 throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value)); 343 throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value));
249 } 344 }
250 catch (FileNotFoundException) 345 catch(FileNotFoundException)
251 { 346 {
252 m_log.Error( 347 m_log.Error(
253 "[TERRAIN]: Unable to load heightmap, file not found. (A directory permissions error may also cause this)"); 348 "[TERRAIN]: Unable to load heightmap, file not found. (A directory permissions error may also cause this)");
254 throw new TerrainException( 349 throw new TerrainException(
255 String.Format("unable to load heightmap: file {0} not found (or permissions do not allow access", filename)); 350 String.Format("unable to load heightmap: file {0} not found (or permissions do not allow access", filename));
256 } 351 }
257 catch (ArgumentException e) 352 catch(ArgumentException e)
258 { 353 {
259 m_log.ErrorFormat("[TERRAIN]: Unable to load heightmap: {0}", e.Message); 354 m_log.ErrorFormat("[TERRAIN]: Unable to load heightmap: {0}", e.Message);
260 throw new TerrainException( 355 throw new TerrainException(
261 String.Format("Unable to load heightmap: {0}", e.Message)); 356 String.Format("Unable to load heightmap: {0}", e.Message));
262 } 357 }
263 } 358 }
264 CheckForTerrainUpdates();
265 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); 359 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
266 return; 360 return;
267 } 361 }
@@ -279,7 +373,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
279 { 373 {
280 try 374 try
281 { 375 {
282 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) 376 foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders)
283 { 377 {
284 if (filename.EndsWith(loader.Key)) 378 if (filename.EndsWith(loader.Key))
285 { 379 {
@@ -289,7 +383,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
289 } 383 }
290 } 384 }
291 } 385 }
292 catch (IOException ioe) 386 catch(IOException ioe)
293 { 387 {
294 m_log.Error(String.Format("[TERRAIN]: Unable to save to {0}, {1}", filename, ioe.Message)); 388 m_log.Error(String.Format("[TERRAIN]: Unable to save to {0}, {1}", filename, ioe.Message));
295 } 389 }
@@ -309,27 +403,32 @@ namespace OpenSim.Region.CoreModules.World.Terrain
309 LoadFromStream(filename, URIFetch(pathToTerrainHeightmap)); 403 LoadFromStream(filename, URIFetch(pathToTerrainHeightmap));
310 } 404 }
311 405
406 public void LoadFromStream(string filename, Stream stream)
407 {
408 LoadFromStream(filename, Vector3.Zero, 0f, Vector2.Zero, stream);
409 }
410
312 /// <summary> 411 /// <summary>
313 /// Loads a terrain file from a stream and installs it in the scene. 412 /// Loads a terrain file from a stream and installs it in the scene.
314 /// </summary> 413 /// </summary>
315 /// <param name="filename">Filename to terrain file. Type is determined by extension.</param> 414 /// <param name="filename">Filename to terrain file. Type is determined by extension.</param>
316 /// <param name="stream"></param> 415 /// <param name="stream"></param>
317 public void LoadFromStream(string filename, Stream stream) 416 public void LoadFromStream(string filename, Vector3 displacement,
417 float radianRotation, Vector2 rotationDisplacement, Stream stream)
318 { 418 {
319 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) 419 foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders)
320 { 420 {
321 if (filename.EndsWith(loader.Key)) 421 if (filename.EndsWith(loader.Key))
322 { 422 {
323 lock (m_scene) 423 lock(m_scene)
324 { 424 {
325 try 425 try
326 { 426 {
327 ITerrainChannel channel = loader.Value.LoadStream(stream); 427 ITerrainChannel channel = loader.Value.LoadStream(stream);
328 m_scene.Heightmap = channel; 428 m_channel.Merge(channel, displacement, radianRotation, rotationDisplacement);
329 m_channel = channel;
330 UpdateRevertMap(); 429 UpdateRevertMap();
331 } 430 }
332 catch (NotImplementedException) 431 catch(NotImplementedException)
333 { 432 {
334 m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value + 433 m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value +
335 " parser does not support file loading. (May be save only)"); 434 " parser does not support file loading. (May be save only)");
@@ -337,7 +436,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
337 } 436 }
338 } 437 }
339 438
340 CheckForTerrainUpdates();
341 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); 439 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
342 return; 440 return;
343 } 441 }
@@ -390,7 +488,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
390 { 488 {
391 try 489 try
392 { 490 {
393 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) 491 foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders)
394 { 492 {
395 if (filename.EndsWith(loader.Key)) 493 if (filename.EndsWith(loader.Key))
396 { 494 {
@@ -399,18 +497,56 @@ namespace OpenSim.Region.CoreModules.World.Terrain
399 } 497 }
400 } 498 }
401 } 499 }
402 catch (NotImplementedException) 500 catch(NotImplementedException)
403 { 501 {
404 m_log.Error("Unable to save to " + filename + ", saving of this file format has not been implemented."); 502 m_log.Error("Unable to save to " + filename + ", saving of this file format has not been implemented.");
405 throw new TerrainException(String.Format("Unable to save heightmap: saving of this file format not implemented")); 503 throw new TerrainException(String.Format("Unable to save heightmap: saving of this file format not implemented"));
406 } 504 }
407 } 505 }
408 506
409 public void TaintTerrain () 507 // Someone diddled terrain outside the normal code paths. Set the taintedness for all clients.
508 // ITerrainModule.TaintTerrain()
509 public void TaintTerrain()
410 { 510 {
411 CheckForTerrainUpdates(); 511 lock(m_perClientPatchUpdates)
512 {
513 // Set the flags for all clients so the tainted patches will be sent out
514 foreach(PatchUpdates pups in m_perClientPatchUpdates.Values)
515 {
516 pups.SetAll(m_scene.Heightmap.GetTerrainData());
517 }
518 }
412 } 519 }
413 520
521 // ITerrainModule.PushTerrain()
522 public void PushTerrain(IClientAPI pClient)
523 {
524 // If view distance based, set the modified patch bits and the frame event will send the updates
525 if (m_sendTerrainUpdatesByViewDistance)
526 {
527 ScenePresence presence = m_scene.GetScenePresence(pClient.AgentId);
528 if (presence != null)
529 {
530 lock(m_perClientPatchUpdates)
531 {
532 PatchUpdates pups;
533 if (!m_perClientPatchUpdates.TryGetValue(pClient.AgentId, out pups))
534 {
535 // There is a ScenePresence without a send patch map. Create one.
536 pups = new PatchUpdates(m_scene.Heightmap.GetTerrainData(), presence);
537 m_perClientPatchUpdates.Add(presence.UUID, pups);
538 }
539 // By setting all to modified, the next update tick will send the patches
540 pups.SetAll(true);
541 }
542 }
543 }
544 else
545 {
546 // The traditional way is to call into the protocol stack to send them all.
547 pClient.SendLayerData(new float[10]);
548 }
549 }
414 #region Plugin Loading Methods 550 #region Plugin Loading Methods
415 551
416 private void LoadPlugins() 552 private void LoadPlugins()
@@ -418,13 +554,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain
418 m_plugineffects = new Dictionary<string, ITerrainEffect>(); 554 m_plugineffects = new Dictionary<string, ITerrainEffect>();
419 LoadPlugins(Assembly.GetCallingAssembly()); 555 LoadPlugins(Assembly.GetCallingAssembly());
420 string plugineffectsPath = "Terrain"; 556 string plugineffectsPath = "Terrain";
421 557
422 // Load the files in the Terrain/ dir 558 // Load the files in the Terrain/ dir
423 if (!Directory.Exists(plugineffectsPath)) 559 if (!Directory.Exists(plugineffectsPath))
424 return; 560 return;
425 561
426 string[] files = Directory.GetFiles(plugineffectsPath); 562 string[] files = Directory.GetFiles(plugineffectsPath);
427 foreach (string file in files) 563 foreach(string file in files)
428 { 564 {
429 m_log.Info("Loading effects in " + file); 565 m_log.Info("Loading effects in " + file);
430 try 566 try
@@ -432,7 +568,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
432 Assembly library = Assembly.LoadFrom(file); 568 Assembly library = Assembly.LoadFrom(file);
433 LoadPlugins(library); 569 LoadPlugins(library);
434 } 570 }
435 catch (BadImageFormatException) 571 catch(BadImageFormatException)
436 { 572 {
437 } 573 }
438 } 574 }
@@ -440,7 +576,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
440 576
441 private void LoadPlugins(Assembly library) 577 private void LoadPlugins(Assembly library)
442 { 578 {
443 foreach (Type pluginType in library.GetTypes()) 579 foreach(Type pluginType in library.GetTypes())
444 { 580 {
445 try 581 try
446 { 582 {
@@ -462,7 +598,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
462 m_log.Info("L ... " + typeName); 598 m_log.Info("L ... " + typeName);
463 } 599 }
464 } 600 }
465 catch (AmbiguousMatchException) 601 catch(AmbiguousMatchException)
466 { 602 {
467 } 603 }
468 } 604 }
@@ -470,7 +606,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
470 606
471 public void InstallPlugin(string pluginName, ITerrainEffect effect) 607 public void InstallPlugin(string pluginName, ITerrainEffect effect)
472 { 608 {
473 lock (m_plugineffects) 609 lock(m_plugineffects)
474 { 610 {
475 if (!m_plugineffects.ContainsKey(pluginName)) 611 if (!m_plugineffects.ContainsKey(pluginName))
476 { 612 {
@@ -513,6 +649,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain
513 m_floodeffects[StandardTerrainEffects.Flatten] = new FlattenArea(); 649 m_floodeffects[StandardTerrainEffects.Flatten] = new FlattenArea();
514 m_floodeffects[StandardTerrainEffects.Revert] = new RevertArea(m_revert); 650 m_floodeffects[StandardTerrainEffects.Revert] = new RevertArea(m_revert);
515 651
652 // Terrain Modifier operations
653 m_modifyOperations["min"] = new MinModifier(this);
654 m_modifyOperations["max"] = new MaxModifier(this);
655 m_modifyOperations["raise"] = new RaiseModifier(this);
656 m_modifyOperations["lower"] = new LowerModifier(this);
657 m_modifyOperations["fill"] = new FillModifier(this);
658 m_modifyOperations["smooth"] = new SmoothModifier(this);
659 m_modifyOperations["noise"] = new NoiseModifier(this);
660
516 // Filesystem load/save loaders 661 // Filesystem load/save loaders
517 m_loaders[".r32"] = new RAW32(); 662 m_loaders[".r32"] = new RAW32();
518 m_loaders[".f32"] = m_loaders[".r32"]; 663 m_loaders[".f32"] = m_loaders[".r32"];
@@ -532,6 +677,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
532 /// </summary> 677 /// </summary>
533 public void UpdateRevertMap() 678 public void UpdateRevertMap()
534 { 679 {
680 /*
535 int x; 681 int x;
536 for (x = 0; x < m_channel.Width; x++) 682 for (x = 0; x < m_channel.Width; x++)
537 { 683 {
@@ -541,6 +687,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
541 m_revert[x, y] = m_channel[x, y]; 687 m_revert[x, y] = m_channel[x, y];
542 } 688 }
543 } 689 }
690 */
691 m_revert = m_channel.MakeCopy();
544 } 692 }
545 693
546 /// <summary> 694 /// <summary>
@@ -553,22 +701,22 @@ namespace OpenSim.Region.CoreModules.World.Terrain
553 /// <param name="fileStartY">Where to begin our slice</param> 701 /// <param name="fileStartY">Where to begin our slice</param>
554 public void LoadFromFile(string filename, int fileWidth, int fileHeight, int fileStartX, int fileStartY) 702 public void LoadFromFile(string filename, int fileWidth, int fileHeight, int fileStartX, int fileStartY)
555 { 703 {
556 int offsetX = (int) m_scene.RegionInfo.RegionLocX - fileStartX; 704 int offsetX = (int)m_scene.RegionInfo.RegionLocX - fileStartX;
557 int offsetY = (int) m_scene.RegionInfo.RegionLocY - fileStartY; 705 int offsetY = (int)m_scene.RegionInfo.RegionLocY - fileStartY;
558 706
559 if (offsetX >= 0 && offsetX < fileWidth && offsetY >= 0 && offsetY < fileHeight) 707 if (offsetX >= 0 && offsetX < fileWidth && offsetY >= 0 && offsetY < fileHeight)
560 { 708 {
561 // this region is included in the tile request 709 // this region is included in the tile request
562 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) 710 foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders)
563 { 711 {
564 if (filename.EndsWith(loader.Key)) 712 if (filename.EndsWith(loader.Key))
565 { 713 {
566 lock (m_scene) 714 lock(m_scene)
567 { 715 {
568 ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY, 716 ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY,
569 fileWidth, fileHeight, 717 fileWidth, fileHeight,
570 (int) Constants.RegionSize, 718 (int)m_scene.RegionInfo.RegionSizeX,
571 (int) Constants.RegionSize); 719 (int)m_scene.RegionInfo.RegionSizeY);
572 m_scene.Heightmap = channel; 720 m_scene.Heightmap = channel;
573 m_channel = channel; 721 m_channel = channel;
574 UpdateRevertMap(); 722 UpdateRevertMap();
@@ -607,23 +755,23 @@ namespace OpenSim.Region.CoreModules.World.Terrain
607 } 755 }
608 756
609 // this region is included in the tile request 757 // this region is included in the tile request
610 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) 758 foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders)
611 { 759 {
612 if (filename.EndsWith(loader.Key) && loader.Value.SupportsTileSave()) 760 if (filename.EndsWith(loader.Key) && loader.Value.SupportsTileSave())
613 { 761 {
614 lock (m_scene) 762 lock(m_scene)
615 { 763 {
616 loader.Value.SaveFile(m_channel, filename, offsetX, offsetY, 764 loader.Value.SaveFile(m_channel, filename, offsetX, offsetY,
617 fileWidth, fileHeight, 765 fileWidth, fileHeight,
618 (int)Constants.RegionSize, 766 (int)m_scene.RegionInfo.RegionSizeX,
619 (int)Constants.RegionSize); 767 (int)m_scene.RegionInfo.RegionSizeY);
620 768
621 MainConsole.Instance.OutputFormat( 769 MainConsole.Instance.OutputFormat(
622 "Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}", 770 "Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}",
623 fileStartX, fileStartY, fileStartX + fileWidth - 1, fileStartY + fileHeight - 1, 771 fileStartX, fileStartY, fileStartX + fileWidth - 1, fileStartY + fileHeight - 1,
624 m_scene.RegionInfo.RegionName, filename); 772 m_scene.RegionInfo.RegionName, filename);
625 } 773 }
626 774
627 return; 775 return;
628 } 776 }
629 } 777 }
@@ -634,7 +782,44 @@ namespace OpenSim.Region.CoreModules.World.Terrain
634 } 782 }
635 783
636 /// <summary> 784 /// <summary>
785 /// Called before processing of every simulation frame.
786 /// This is used to check to see of any of the terrain is tainted and, if so, schedule
787 /// updates for all the presences.
788 /// This also checks to see if there are updates that need to be sent for each presence.
789 /// This is where the logic is to send terrain updates to clients.
790 /// </summary>
791 private void EventManager_OnFrame()
792 {
793 TerrainData terrData = m_channel.GetTerrainData();
794
795 bool shouldTaint = false;
796 for(int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize)
797 {
798 for(int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize)
799 {
800 if (terrData.IsTaintedAt(x, y))
801 {
802 // Found a patch that was modified. Push this flag into the clients.
803 SendToClients(terrData, x, y);
804 shouldTaint = true;
805 }
806 }
807 }
808
809 // This event also causes changes to be sent to the clients
810 CheckSendingPatchesToClients();
811
812 // If things changes, generate some events
813 if (shouldTaint)
814 {
815 m_scene.EventManager.TriggerTerrainTainted();
816 m_tainted = true;
817 }
818 }
819
820 /// <summary>
637 /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections 821 /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections
822 /// Called infrequently (like every 5 seconds or so). Best used for storing terrain.
638 /// </summary> 823 /// </summary>
639 private void EventManager_OnTerrainTick() 824 private void EventManager_OnTerrainTick()
640 { 825 {
@@ -665,7 +850,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
665 850
666 string[] tmpArgs = new string[args.Length - 2]; 851 string[] tmpArgs = new string[args.Length - 2];
667 int i; 852 int i;
668 for (i = 2; i < args.Length; i++) 853 for(i = 2; i < args.Length; i++)
669 tmpArgs[i - 2] = args[i]; 854 tmpArgs[i - 2] = args[i];
670 855
671 m_commander.ProcessConsoleCommand(args[1], tmpArgs); 856 m_commander.ProcessConsoleCommand(args[1], tmpArgs);
@@ -683,56 +868,50 @@ namespace OpenSim.Region.CoreModules.World.Terrain
683 client.OnLandUndo += client_OnLandUndo; 868 client.OnLandUndo += client_OnLandUndo;
684 client.OnUnackedTerrain += client_OnUnackedTerrain; 869 client.OnUnackedTerrain += client_OnUnackedTerrain;
685 } 870 }
686 871
687 /// <summary> 872 /// <summary>
688 /// Checks to see if the terrain has been modified since last check 873 /// Installs terrain brush hook to IClientAPI
689 /// but won't attempt to limit those changes to the limits specified in the estate settings
690 /// currently invoked by the command line operations in the region server only
691 /// </summary> 874 /// </summary>
692 private void CheckForTerrainUpdates() 875 /// <param name="client"></param>
876 private void EventManager_OnClientClosed(UUID client, Scene scene)
693 { 877 {
694 CheckForTerrainUpdates(false); 878 ScenePresence presence = scene.GetScenePresence(client);
879 if (presence != null)
880 {
881 presence.ControllingClient.OnModifyTerrain -= client_OnModifyTerrain;
882 presence.ControllingClient.OnBakeTerrain -= client_OnBakeTerrain;
883 presence.ControllingClient.OnLandUndo -= client_OnLandUndo;
884 presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain;
885 }
886
887 lock(m_perClientPatchUpdates)
888 m_perClientPatchUpdates.Remove(client);
695 } 889 }
696 890
697 /// <summary> 891 /// <summary>
698 /// Checks to see if the terrain has been modified since last check. 892 /// Scan over changes in the terrain and limit height changes. This enforces the
699 /// If it has been modified, every all the terrain patches are sent to the client. 893 /// non-estate owner limits on rate of terrain editting.
700 /// If the call is asked to respect the estate settings for terrain_raise_limit and 894 /// Returns 'true' if any heights were limited.
701 /// terrain_lower_limit, it will clamp terrain updates between these values
702 /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces
703 /// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param>
704 /// </summary> 895 /// </summary>
705 private void CheckForTerrainUpdates(bool respectEstateSettings) 896 private bool EnforceEstateLimits()
706 { 897 {
707 bool shouldTaint = false; 898 TerrainData terrData = m_channel.GetTerrainData();
708 float[] serialised = m_channel.GetFloatsSerialised(); 899
709 int x; 900 bool wasLimited = false;
710 for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize) 901 for(int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize)
711 { 902 {
712 int y; 903 for(int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize)
713 for (y = 0; y < m_channel.Height; y += Constants.TerrainPatchSize)
714 { 904 {
715 if (m_channel.Tainted(x, y)) 905 if (terrData.IsTaintedAt(x, y, false /* clearOnTest */))
716 { 906 {
717 // if we should respect the estate settings then 907 // If we should respect the estate settings then
718 // fixup and height deltas that don't respect them 908 // fixup and height deltas that don't respect them.
719 if (respectEstateSettings && LimitChannelChanges(x, y)) 909 // Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values.
720 { 910 wasLimited |= LimitChannelChanges(terrData, x, y);
721 // this has been vetoed, so update
722 // what we are going to send to the client
723 serialised = m_channel.GetFloatsSerialised();
724 }
725
726 SendToClients(serialised, x, y);
727 shouldTaint = true;
728 } 911 }
729 } 912 }
730 } 913 }
731 if (shouldTaint) 914 return wasLimited;
732 {
733 m_scene.EventManager.TriggerTerrainTainted();
734 m_tainted = true;
735 }
736 } 915 }
737 916
738 /// <summary> 917 /// <summary>
@@ -740,31 +919,30 @@ namespace OpenSim.Region.CoreModules.World.Terrain
740 /// are all within the current estate limits 919 /// are all within the current estate limits
741 /// <returns>true if changes were limited, false otherwise</returns> 920 /// <returns>true if changes were limited, false otherwise</returns>
742 /// </summary> 921 /// </summary>
743 private bool LimitChannelChanges(int xStart, int yStart) 922 private bool LimitChannelChanges(TerrainData terrData, int xStart, int yStart)
744 { 923 {
745 bool changesLimited = false; 924 bool changesLimited = false;
746 double minDelta = m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; 925 float minDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit;
747 double maxDelta = m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; 926 float maxDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit;
748 927
749 // loop through the height map for this patch and compare it against 928 // loop through the height map for this patch and compare it against
750 // the revert map 929 // the revert map
751 for (int x = xStart; x < xStart + Constants.TerrainPatchSize; x++) 930 for(int x = xStart; x < xStart + Constants.TerrainPatchSize; x++)
752 { 931 {
753 for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) 932 for(int y = yStart; y < yStart + Constants.TerrainPatchSize; y++)
754 { 933 {
755 934 float requestedHeight = terrData[x, y];
756 double requestedHeight = m_channel[x, y]; 935 float bakedHeight = (float)m_revert[x, y];
757 double bakedHeight = m_revert[x, y]; 936 float requestedDelta = requestedHeight - bakedHeight;
758 double requestedDelta = requestedHeight - bakedHeight;
759 937
760 if (requestedDelta > maxDelta) 938 if (requestedDelta > maxDelta)
761 { 939 {
762 m_channel[x, y] = bakedHeight + maxDelta; 940 terrData[x, y] = bakedHeight + maxDelta;
763 changesLimited = true; 941 changesLimited = true;
764 } 942 }
765 else if (requestedDelta < minDelta) 943 else if (requestedDelta < minDelta)
766 { 944 {
767 m_channel[x, y] = bakedHeight + minDelta; //as lower is a -ve delta 945 terrData[x, y] = bakedHeight + minDelta; //as lower is a -ve delta
768 changesLimited = true; 946 changesLimited = true;
769 } 947 }
770 } 948 }
@@ -775,7 +953,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
775 953
776 private void client_OnLandUndo(IClientAPI client) 954 private void client_OnLandUndo(IClientAPI client)
777 { 955 {
778 lock (m_undo) 956 lock(m_undo)
779 { 957 {
780 if (m_undo.Count > 0) 958 if (m_undo.Count > 0)
781 { 959 {
@@ -792,14 +970,177 @@ namespace OpenSim.Region.CoreModules.World.Terrain
792 /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param> 970 /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param>
793 /// <param name="x">The patch corner to send</param> 971 /// <param name="x">The patch corner to send</param>
794 /// <param name="y">The patch corner to send</param> 972 /// <param name="y">The patch corner to send</param>
795 private void SendToClients(float[] serialised, int x, int y) 973 private void SendToClients(TerrainData terrData, int x, int y)
796 { 974 {
797 m_scene.ForEachClient( 975 if (m_sendTerrainUpdatesByViewDistance)
798 delegate(IClientAPI controller) 976 {
799 { controller.SendLayerData( 977 // Add that this patch needs to be sent to the accounting for each client.
800 x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, serialised); 978 lock(m_perClientPatchUpdates)
979 {
980 m_scene.ForEachScenePresence(presence =>
981 {
982 PatchUpdates thisClientUpdates;
983 if (!m_perClientPatchUpdates.TryGetValue(presence.UUID, out thisClientUpdates))
984 {
985 // There is a ScenePresence without a send patch map. Create one.
986 thisClientUpdates = new PatchUpdates(terrData, presence);
987 m_perClientPatchUpdates.Add(presence.UUID, thisClientUpdates);
988 }
989 thisClientUpdates.SetByXY(x, y, true);
801 } 990 }
802 ); 991 );
992 }
993 }
994 else
995 {
996 // Legacy update sending where the update is sent out as soon as noticed
997 // We know the actual terrain data that is passed is ignored so this passes a dummy heightmap.
998 //float[] heightMap = terrData.GetFloatsSerialized();
999 float[] heightMap = new float[10];
1000 m_scene.ForEachClient(
1001 delegate(IClientAPI controller)
1002 {
1003 controller.SendLayerData(x / Constants.TerrainPatchSize,
1004 y / Constants.TerrainPatchSize,
1005 heightMap);
1006 }
1007 );
1008 }
1009 }
1010
1011 private class PatchesToSend : IComparable<PatchesToSend>
1012 {
1013 public int PatchX;
1014 public int PatchY;
1015 public float Dist;
1016
1017 public PatchesToSend(int pX, int pY, float pDist)
1018 {
1019 PatchX = pX;
1020 PatchY = pY;
1021 Dist = pDist;
1022 }
1023
1024 public int CompareTo(PatchesToSend other)
1025 {
1026 return Dist.CompareTo(other.Dist);
1027 }
1028 }
1029
1030 // Called each frame time to see if there are any patches to send to any of the
1031 // ScenePresences.
1032 // We know this is only called if we are doing view distance patch sending so some
1033 // tests are not made.
1034 // Loop through all the per-client info and send any patches necessary.
1035 private void CheckSendingPatchesToClients()
1036 {
1037 lock(m_perClientPatchUpdates)
1038 {
1039 foreach(PatchUpdates pups in m_perClientPatchUpdates.Values)
1040 {
1041 if (pups.HasUpdates())
1042 {
1043 // There is something that could be sent to this client.
1044 List<PatchesToSend> toSend = GetModifiedPatchesInViewDistance(pups);
1045 if (toSend.Count > 0)
1046 {
1047 // m_log.DebugFormat("{0} CheckSendingPatchesToClient: sending {1} patches to {2} in region {3}",
1048 // LogHeader, toSend.Count, pups.Presence.Name, m_scene.RegionInfo.RegionName);
1049 // Sort the patches to send by the distance from the presence
1050 toSend.Sort();
1051 /* old way that sent individual patches
1052 foreach (PatchesToSend pts in toSend)
1053 {
1054 pups.Presence.ControllingClient.SendLayerData(pts.PatchX, pts.PatchY, null);
1055 // presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), null, TerrainPatch.LayerType.Land);
1056 }
1057 */
1058
1059 // new way that sends all patches to the protocol so they can be sent in one block
1060 int[] xPieces = new int[toSend.Count];
1061 int[] yPieces = new int[toSend.Count];
1062 float[] patchPieces = new float[toSend.Count * 2];
1063 int pieceIndex = 0;
1064 foreach(PatchesToSend pts in toSend)
1065 {
1066 patchPieces[pieceIndex++] = pts.PatchX;
1067 patchPieces[pieceIndex++] = pts.PatchY;
1068 }
1069 pups.Presence.ControllingClient.SendLayerData(-toSend.Count, 0, patchPieces);
1070 }
1071 }
1072 }
1073 }
1074 }
1075
1076 // Compute a list of modified patches that are within our view distance.
1077 private List<PatchesToSend> GetModifiedPatchesInViewDistance(PatchUpdates pups)
1078 {
1079 List<PatchesToSend> ret = new List<PatchesToSend>();
1080
1081 ScenePresence presence = pups.Presence;
1082 if (presence == null)
1083 return ret;
1084
1085 Vector3 presencePos = presence.AbsolutePosition;
1086
1087 // Before this distance check, the whole region just showed up. Adding the distance
1088 // check causes different things to happen for the current and adjacent regions.
1089 // So, to keep legacy views, if the region is legacy sized, don't do distance check.
1090 bool isLegacySizedRegion = pups.Terrain.SizeX == Constants.RegionSize && pups.Terrain.SizeY == Constants.RegionSize;
1091 bool shouldCheckViewDistance = m_sendTerrainUpdatesByViewDistance && !isLegacySizedRegion;
1092
1093 int startX = 0;
1094 int endX = (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize;
1095 int startY = 0;
1096 int endY = (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize;
1097
1098 // The following only reduces the size of area scanned for updates. Only significant for very large varregions.
1099 if (shouldCheckViewDistance)
1100 {
1101 // Compute the area of patches within our draw distance
1102 startX = (((int)(presencePos.X - presence.DrawDistance)) / Constants.TerrainPatchSize) - 2;
1103 startX = Math.Max(startX, 0);
1104 startX = Math.Min(startX, (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize);
1105 startY = (((int)(presencePos.Y - presence.DrawDistance)) / Constants.TerrainPatchSize) - 2;
1106 startY = Math.Max(startY, 0);
1107 startY = Math.Min(startY, (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize);
1108 endX = (((int)(presencePos.X + presence.DrawDistance)) / Constants.TerrainPatchSize) + 2;
1109 endX = Math.Max(endX, 0);
1110 endX = Math.Min(endX, (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize);
1111 endY = (((int)(presencePos.Y + presence.DrawDistance)) / Constants.TerrainPatchSize) + 2;
1112 endY = Math.Max(endY, 0);
1113 endY = Math.Min(endY, (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize);
1114 }
1115
1116 // m_log.DebugFormat("{0} GetModifiedPatchesInViewDistance. rName={1}, ddist={2}, apos={3}, cpos={4}, isChild={5}, start=<{6},{7}>, end=<{8},{9}>",
1117 // LogHeader, m_scene.RegionInfo.RegionName,
1118 // presence.DrawDistance, presencePos, presence.CameraPosition,
1119 // isLegacySizeChildRegion,
1120 // startX, startY, endX, endY);
1121 for(int x = startX; x < endX; x++)
1122 {
1123 for(int y = startY; y < endY; y++)
1124 {
1125 //Need to make sure we don't send the same ones over and over
1126 Vector3 patchPos = new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, presencePos.Z);
1127 if (pups.GetByPatch(x, y))
1128 {
1129 //Check which has less distance, camera or avatar position, both have to be done.
1130 //Its not a radius, its a diameter and we add 50 so that it doesn't look like it cuts off
1131 if (!shouldCheckViewDistance
1132 || Util.DistanceLessThan(presencePos, patchPos, presence.DrawDistance + 50)
1133 || Util.DistanceLessThan(presence.CameraPosition, patchPos, presence.DrawDistance + 50))
1134 {
1135 //They can see it, send it to them
1136 pups.SetByPatch(x, y, false);
1137 float dist = Vector3.DistanceSquared(presencePos, patchPos);
1138 ret.Add(new PatchesToSend(x, y, dist));
1139 }
1140 }
1141 }
1142 }
1143 return ret;
803 } 1144 }
804 1145
805 private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, 1146 private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action,
@@ -809,28 +1150,28 @@ namespace OpenSim.Region.CoreModules.World.Terrain
809 bool allowed = false; 1150 bool allowed = false;
810 if (north == south && east == west) 1151 if (north == south && east == west)
811 { 1152 {
812 if (m_painteffects.ContainsKey((StandardTerrainEffects) action)) 1153 if (m_painteffects.ContainsKey((StandardTerrainEffects)action))
813 { 1154 {
814 bool[,] allowMask = new bool[m_channel.Width,m_channel.Height]; 1155 bool[,] allowMask = new bool[m_channel.Width, m_channel.Height];
815 allowMask.Initialize(); 1156 allowMask.Initialize();
816 int n = size + 1; 1157 int n = size + 1;
817 if (n > 2) 1158 if (n > 2)
818 n = 4; 1159 n = 4;
819 1160
820 int zx = (int) (west + 0.5); 1161 int zx = (int)(west + 0.5);
821 int zy = (int) (north + 0.5); 1162 int zy = (int)(north + 0.5);
822 1163
823 int dx; 1164 int dx;
824 for (dx=-n; dx<=n; dx++) 1165 for(dx=-n; dx<=n; dx++)
825 { 1166 {
826 int dy; 1167 int dy;
827 for (dy=-n; dy<=n; dy++) 1168 for(dy=-n; dy<=n; dy++)
828 { 1169 {
829 int x = zx + dx; 1170 int x = zx + dx;
830 int y = zy + dy; 1171 int y = zy + dy;
831 if (x>=0 && y>=0 && x<m_channel.Width && y<m_channel.Height) 1172 if (x >= 0 && y >= 0 && x < m_channel.Width && y < m_channel.Height)
832 { 1173 {
833 if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x,y,0))) 1174 if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x, y, 0)))
834 { 1175 {
835 allowMask[x, y] = true; 1176 allowMask[x, y] = true;
836 allowed = true; 1177 allowed = true;
@@ -841,10 +1182,12 @@ namespace OpenSim.Region.CoreModules.World.Terrain
841 if (allowed) 1182 if (allowed)
842 { 1183 {
843 StoreUndoState(); 1184 StoreUndoState();
844 m_painteffects[(StandardTerrainEffects) action].PaintEffect( 1185 m_painteffects[(StandardTerrainEffects)action].PaintEffect(
845 m_channel, allowMask, west, south, height, size, seconds); 1186 m_channel, allowMask, west, south, height, size, seconds);
846 1187
847 CheckForTerrainUpdates(!god); //revert changes outside estate limits 1188 //revert changes outside estate limits
1189 if (!god)
1190 EnforceEstateLimits();
848 } 1191 }
849 } 1192 }
850 else 1193 else
@@ -854,22 +1197,22 @@ namespace OpenSim.Region.CoreModules.World.Terrain
854 } 1197 }
855 else 1198 else
856 { 1199 {
857 if (m_floodeffects.ContainsKey((StandardTerrainEffects) action)) 1200 if (m_floodeffects.ContainsKey((StandardTerrainEffects)action))
858 { 1201 {
859 bool[,] fillArea = new bool[m_channel.Width,m_channel.Height]; 1202 bool[,] fillArea = new bool[m_channel.Width, m_channel.Height];
860 fillArea.Initialize(); 1203 fillArea.Initialize();
861 1204
862 int x; 1205 int x;
863 for (x = 0; x < m_channel.Width; x++) 1206 for(x = 0; x < m_channel.Width; x++)
864 { 1207 {
865 int y; 1208 int y;
866 for (y = 0; y < m_channel.Height; y++) 1209 for(y = 0; y < m_channel.Height; y++)
867 { 1210 {
868 if (x < east && x > west) 1211 if (x < east && x > west)
869 { 1212 {
870 if (y < north && y > south) 1213 if (y < north && y > south)
871 { 1214 {
872 if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x,y,0))) 1215 if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x, y, 0)))
873 { 1216 {
874 fillArea[x, y] = true; 1217 fillArea[x, y] = true;
875 allowed = true; 1218 allowed = true;
@@ -882,10 +1225,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
882 if (allowed) 1225 if (allowed)
883 { 1226 {
884 StoreUndoState(); 1227 StoreUndoState();
885 m_floodeffects[(StandardTerrainEffects) action].FloodEffect( 1228 m_floodeffects[(StandardTerrainEffects)action].FloodEffect(m_channel, fillArea, size);
886 m_channel, fillArea, size);
887 1229
888 CheckForTerrainUpdates(!god); //revert changes outside estate limits 1230 //revert changes outside estate limits
1231 if (!god)
1232 EnforceEstateLimits();
889 } 1233 }
890 } 1234 }
891 else 1235 else
@@ -905,16 +1249,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain
905 InterfaceBakeTerrain(null); //bake terrain does not use the passed in parameter 1249 InterfaceBakeTerrain(null); //bake terrain does not use the passed in parameter
906 } 1250 }
907 } 1251 }
908 1252
909 protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY) 1253 protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY)
910 { 1254 {
911 //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY); 1255 //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY);
912 client.SendLayerData(patchX, patchY, m_scene.Heightmap.GetFloatsSerialised()); 1256 // SendLayerData does not use the heightmap parameter. This kludge is so as to not change IClientAPI.
1257 float[] heightMap = new float[10];
1258 client.SendLayerData(patchX, patchY, heightMap);
913 } 1259 }
914 1260
915 private void StoreUndoState() 1261 private void StoreUndoState()
916 { 1262 {
917 lock (m_undo) 1263 lock(m_undo)
918 { 1264 {
919 if (m_undo.Count > 0) 1265 if (m_undo.Count > 0)
920 { 1266 {
@@ -935,23 +1281,21 @@ namespace OpenSim.Region.CoreModules.World.Terrain
935 1281
936 private void InterfaceLoadFile(Object[] args) 1282 private void InterfaceLoadFile(Object[] args)
937 { 1283 {
938 LoadFromFile((string) args[0]); 1284 LoadFromFile((string)args[0]);
939 CheckForTerrainUpdates();
940 } 1285 }
941 1286
942 private void InterfaceLoadTileFile(Object[] args) 1287 private void InterfaceLoadTileFile(Object[] args)
943 { 1288 {
944 LoadFromFile((string) args[0], 1289 LoadFromFile((string)args[0],
945 (int) args[1], 1290 (int)args[1],
946 (int) args[2], 1291 (int)args[2],
947 (int) args[3], 1292 (int)args[3],
948 (int) args[4]); 1293 (int)args[4]);
949 CheckForTerrainUpdates();
950 } 1294 }
951 1295
952 private void InterfaceSaveFile(Object[] args) 1296 private void InterfaceSaveFile(Object[] args)
953 { 1297 {
954 SaveToFile((string) args[0]); 1298 SaveToFile((string)args[0]);
955 } 1299 }
956 1300
957 private void InterfaceSaveTileFile(Object[] args) 1301 private void InterfaceSaveTileFile(Object[] args)
@@ -971,11 +1315,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
971 private void InterfaceRevertTerrain(Object[] args) 1315 private void InterfaceRevertTerrain(Object[] args)
972 { 1316 {
973 int x, y; 1317 int x, y;
974 for (x = 0; x < m_channel.Width; x++) 1318 for(x = 0; x < m_channel.Width; x++)
975 for (y = 0; y < m_channel.Height; y++) 1319 for(y = 0; y < m_channel.Height; y++)
976 m_channel[x, y] = m_revert[x, y]; 1320 m_channel[x, y] = m_revert[x, y];
977 1321
978 CheckForTerrainUpdates();
979 } 1322 }
980 1323
981 private void InterfaceFlipTerrain(Object[] args) 1324 private void InterfaceFlipTerrain(Object[] args)
@@ -984,39 +1327,36 @@ namespace OpenSim.Region.CoreModules.World.Terrain
984 1327
985 if (direction.ToLower().StartsWith("y")) 1328 if (direction.ToLower().StartsWith("y"))
986 { 1329 {
987 for (int x = 0; x < Constants.RegionSize; x++) 1330 for(int x = 0; x < m_channel.Width; x++)
988 { 1331 {
989 for (int y = 0; y < Constants.RegionSize / 2; y++) 1332 for(int y = 0; y < m_channel.Height / 2; y++)
990 { 1333 {
991 double height = m_channel[x, y]; 1334 double height = m_channel[x, y];
992 double flippedHeight = m_channel[x, (int)Constants.RegionSize - 1 - y]; 1335 double flippedHeight = m_channel[x, (int)m_channel.Height - 1 - y];
993 m_channel[x, y] = flippedHeight; 1336 m_channel[x, y] = flippedHeight;
994 m_channel[x, (int)Constants.RegionSize - 1 - y] = height; 1337 m_channel[x, (int)m_channel.Height - 1 - y] = height;
995 1338
996 } 1339 }
997 } 1340 }
998 } 1341 }
999 else if (direction.ToLower().StartsWith("x")) 1342 else if (direction.ToLower().StartsWith("x"))
1000 { 1343 {
1001 for (int y = 0; y < Constants.RegionSize; y++) 1344 for(int y = 0; y < m_channel.Height; y++)
1002 { 1345 {
1003 for (int x = 0; x < Constants.RegionSize / 2; x++) 1346 for(int x = 0; x < m_channel.Width / 2; x++)
1004 { 1347 {
1005 double height = m_channel[x, y]; 1348 double height = m_channel[x, y];
1006 double flippedHeight = m_channel[(int)Constants.RegionSize - 1 - x, y]; 1349 double flippedHeight = m_channel[(int)m_channel.Width - 1 - x, y];
1007 m_channel[x, y] = flippedHeight; 1350 m_channel[x, y] = flippedHeight;
1008 m_channel[(int)Constants.RegionSize - 1 - x, y] = height; 1351 m_channel[(int)m_channel.Width - 1 - x, y] = height;
1009 1352
1010 } 1353 }
1011 } 1354 }
1012 } 1355 }
1013 else 1356 else
1014 { 1357 {
1015 m_log.Error("Unrecognised direction - need x or y"); 1358 MainConsole.Instance.OutputFormat("ERROR: Unrecognised direction {0} - need x or y", direction);
1016 } 1359 }
1017
1018
1019 CheckForTerrainUpdates();
1020 } 1360 }
1021 1361
1022 private void InterfaceRescaleTerrain(Object[] args) 1362 private void InterfaceRescaleTerrain(Object[] args)
@@ -1042,9 +1382,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1042 int width = m_channel.Width; 1382 int width = m_channel.Width;
1043 int height = m_channel.Height; 1383 int height = m_channel.Height;
1044 1384
1045 for (int x = 0; x < width; x++) 1385 for(int x = 0; x < width; x++)
1046 { 1386 {
1047 for (int y = 0; y < height; y++) 1387 for(int y = 0; y < height; y++)
1048 { 1388 {
1049 double currHeight = m_channel[x, y]; 1389 double currHeight = m_channel[x, y];
1050 if (currHeight < currMin) 1390 if (currHeight < currMin)
@@ -1065,16 +1405,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1065 //m_log.InfoFormat("Scale = {0}", scale); 1405 //m_log.InfoFormat("Scale = {0}", scale);
1066 1406
1067 // scale the heightmap accordingly 1407 // scale the heightmap accordingly
1068 for (int x = 0; x < width; x++) 1408 for(int x = 0; x < width; x++)
1069 { 1409 {
1070 for (int y = 0; y < height; y++) 1410 for(int y = 0; y < height; y++)
1071 { 1411 {
1072 double currHeight = m_channel[x, y] - currMin; 1412 double currHeight = m_channel[x, y] - currMin;
1073 m_channel[x, y] = desiredMin + (currHeight * scale); 1413 m_channel[x, y] = desiredMin + (currHeight * scale);
1074 } 1414 }
1075 } 1415 }
1076 1416
1077 CheckForTerrainUpdates();
1078 } 1417 }
1079 1418
1080 } 1419 }
@@ -1082,64 +1421,73 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1082 private void InterfaceElevateTerrain(Object[] args) 1421 private void InterfaceElevateTerrain(Object[] args)
1083 { 1422 {
1084 int x, y; 1423 int x, y;
1085 for (x = 0; x < m_channel.Width; x++) 1424 for(x = 0; x < m_channel.Width; x++)
1086 for (y = 0; y < m_channel.Height; y++) 1425 for(y = 0; y < m_channel.Height; y++)
1087 m_channel[x, y] += (double) args[0]; 1426 m_channel[x, y] += (double)args[0];
1088 CheckForTerrainUpdates();
1089 } 1427 }
1090 1428
1091 private void InterfaceMultiplyTerrain(Object[] args) 1429 private void InterfaceMultiplyTerrain(Object[] args)
1092 { 1430 {
1093 int x, y; 1431 int x, y;
1094 for (x = 0; x < m_channel.Width; x++) 1432 for(x = 0; x < m_channel.Width; x++)
1095 for (y = 0; y < m_channel.Height; y++) 1433 for(y = 0; y < m_channel.Height; y++)
1096 m_channel[x, y] *= (double) args[0]; 1434 m_channel[x, y] *= (double)args[0];
1097 CheckForTerrainUpdates();
1098 } 1435 }
1099 1436
1100 private void InterfaceLowerTerrain(Object[] args) 1437 private void InterfaceLowerTerrain(Object[] args)
1101 { 1438 {
1102 int x, y; 1439 int x, y;
1103 for (x = 0; x < m_channel.Width; x++) 1440 for(x = 0; x < m_channel.Width; x++)
1104 for (y = 0; y < m_channel.Height; y++) 1441 for(y = 0; y < m_channel.Height; y++)
1105 m_channel[x, y] -= (double) args[0]; 1442 m_channel[x, y] -= (double)args[0];
1106 CheckForTerrainUpdates();
1107 } 1443 }
1108 1444
1109 private void InterfaceFillTerrain(Object[] args) 1445 public void InterfaceFillTerrain(Object[] args)
1110 { 1446 {
1111 int x, y; 1447 int x, y;
1112 1448
1113 for (x = 0; x < m_channel.Width; x++) 1449 for(x = 0; x < m_channel.Width; x++)
1114 for (y = 0; y < m_channel.Height; y++) 1450 for(y = 0; y < m_channel.Height; y++)
1115 m_channel[x, y] = (double) args[0]; 1451 m_channel[x, y] = (double)args[0];
1116 CheckForTerrainUpdates();
1117 } 1452 }
1118 1453
1119 private void InterfaceMinTerrain(Object[] args) 1454 private void InterfaceMinTerrain(Object[] args)
1120 { 1455 {
1121 int x, y; 1456 int x, y;
1122 for (x = 0; x < m_channel.Width; x++) 1457 for(x = 0; x < m_channel.Width; x++)
1123 { 1458 {
1124 for (y = 0; y < m_channel.Height; y++) 1459 for(y = 0; y < m_channel.Height; y++)
1125 { 1460 {
1126 m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]); 1461 m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]);
1127 } 1462 }
1128 } 1463 }
1129 CheckForTerrainUpdates();
1130 } 1464 }
1131 1465
1132 private void InterfaceMaxTerrain(Object[] args) 1466 private void InterfaceMaxTerrain(Object[] args)
1133 { 1467 {
1134 int x, y; 1468 int x, y;
1135 for (x = 0; x < m_channel.Width; x++) 1469 for(x = 0; x < m_channel.Width; x++)
1136 { 1470 {
1137 for (y = 0; y < m_channel.Height; y++) 1471 for(y = 0; y < m_channel.Height; y++)
1138 { 1472 {
1139 m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]); 1473 m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]);
1140 } 1474 }
1141 } 1475 }
1142 CheckForTerrainUpdates(); 1476 }
1477
1478 private void InterfaceShow(Object[] args)
1479 {
1480 Vector2 point;
1481
1482 if (!ConsoleUtil.TryParseConsole2DVector((string)args[0], null, out point))
1483 {
1484 Console.WriteLine("ERROR: {0} is not a valid vector", args[0]);
1485 return;
1486 }
1487
1488 double height = m_channel[(int)point.X, (int)point.Y];
1489
1490 Console.WriteLine("Terrain height at {0} is {1}", point, height);
1143 } 1491 }
1144 1492
1145 private void InterfaceShowDebugStats(Object[] args) 1493 private void InterfaceShowDebugStats(Object[] args)
@@ -1149,10 +1497,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1149 double sum = 0; 1497 double sum = 0;
1150 1498
1151 int x; 1499 int x;
1152 for (x = 0; x < m_channel.Width; x++) 1500 for(x = 0; x < m_channel.Width; x++)
1153 { 1501 {
1154 int y; 1502 int y;
1155 for (y = 0; y < m_channel.Height; y++) 1503 for(y = 0; y < m_channel.Height; y++)
1156 { 1504 {
1157 sum += m_channel[x, y]; 1505 sum += m_channel[x, y];
1158 if (max < m_channel[x, y]) 1506 if (max < m_channel[x, y])
@@ -1164,13 +1512,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1164 1512
1165 double avg = sum / (m_channel.Height * m_channel.Width); 1513 double avg = sum / (m_channel.Height * m_channel.Width);
1166 1514
1167 m_log.Info("Channel " + m_channel.Width + "x" + m_channel.Height); 1515 MainConsole.Instance.OutputFormat("Channel {0}x{1}", m_channel.Width, m_channel.Height);
1168 m_log.Info("max/min/avg/sum: " + max + "/" + min + "/" + avg + "/" + sum); 1516 MainConsole.Instance.OutputFormat("max/min/avg/sum: {0}/{1}/{2}/{3}", max, min, avg, sum);
1169 } 1517 }
1170 1518
1171 private void InterfaceEnableExperimentalBrushes(Object[] args) 1519 private void InterfaceEnableExperimentalBrushes(Object[] args)
1172 { 1520 {
1173 if ((bool) args[0]) 1521 if ((bool)args[0])
1174 { 1522 {
1175 m_painteffects[StandardTerrainEffects.Revert] = new WeatherSphere(); 1523 m_painteffects[StandardTerrainEffects.Revert] = new WeatherSphere();
1176 m_painteffects[StandardTerrainEffects.Flatten] = new OlsenSphere(); 1524 m_painteffects[StandardTerrainEffects.Flatten] = new OlsenSphere();
@@ -1185,28 +1533,30 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1185 private void InterfaceRunPluginEffect(Object[] args) 1533 private void InterfaceRunPluginEffect(Object[] args)
1186 { 1534 {
1187 string firstArg = (string)args[0]; 1535 string firstArg = (string)args[0];
1536
1188 if (firstArg == "list") 1537 if (firstArg == "list")
1189 { 1538 {
1190 m_log.Info("List of loaded plugins"); 1539 MainConsole.Instance.Output("List of loaded plugins");
1191 foreach (KeyValuePair<string, ITerrainEffect> kvp in m_plugineffects) 1540 foreach(KeyValuePair<string, ITerrainEffect> kvp in m_plugineffects)
1192 { 1541 {
1193 m_log.Info(kvp.Key); 1542 MainConsole.Instance.Output(kvp.Key);
1194 } 1543 }
1195 return; 1544 return;
1196 } 1545 }
1546
1197 if (firstArg == "reload") 1547 if (firstArg == "reload")
1198 { 1548 {
1199 LoadPlugins(); 1549 LoadPlugins();
1200 return; 1550 return;
1201 } 1551 }
1552
1202 if (m_plugineffects.ContainsKey(firstArg)) 1553 if (m_plugineffects.ContainsKey(firstArg))
1203 { 1554 {
1204 m_plugineffects[firstArg].RunEffect(m_channel); 1555 m_plugineffects[firstArg].RunEffect(m_channel);
1205 CheckForTerrainUpdates();
1206 } 1556 }
1207 else 1557 else
1208 { 1558 {
1209 m_log.Warn("No such plugin effect loaded."); 1559 MainConsole.Instance.Output("WARNING: No such plugin effect {0} loaded.", firstArg);
1210 } 1560 }
1211 } 1561 }
1212 1562
@@ -1295,12 +1645,17 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1295 new Command("stats", CommandIntentions.COMMAND_STATISTICAL, InterfaceShowDebugStats, 1645 new Command("stats", CommandIntentions.COMMAND_STATISTICAL, InterfaceShowDebugStats,
1296 "Shows some information about the regions heightmap for debugging purposes."); 1646 "Shows some information about the regions heightmap for debugging purposes.");
1297 1647
1648 Command showCommand =
1649 new Command("show", CommandIntentions.COMMAND_NON_HAZARDOUS, InterfaceShow,
1650 "Shows terrain height at a given co-ordinate.");
1651 showCommand.AddArgument("point", "point in <x>,<y> format with no spaces (e.g. 45,45)", "String");
1652
1298 Command experimentalBrushesCommand = 1653 Command experimentalBrushesCommand =
1299 new Command("newbrushes", CommandIntentions.COMMAND_HAZARDOUS, InterfaceEnableExperimentalBrushes, 1654 new Command("newbrushes", CommandIntentions.COMMAND_HAZARDOUS, InterfaceEnableExperimentalBrushes,
1300 "Enables experimental brushes which replace the standard terrain brushes. WARNING: This is a debug setting and may be removed at any time."); 1655 "Enables experimental brushes which replace the standard terrain brushes. WARNING: This is a debug setting and may be removed at any time.");
1301 experimentalBrushesCommand.AddArgument("Enabled?", "true / false - Enable new brushes", "Boolean"); 1656 experimentalBrushesCommand.AddArgument("Enabled?", "true / false - Enable new brushes", "Boolean");
1302 1657
1303 //Plugins 1658 // Plugins
1304 Command pluginRunCommand = 1659 Command pluginRunCommand =
1305 new Command("effect", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRunPluginEffect, "Runs a specified plugin effect"); 1660 new Command("effect", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRunPluginEffect, "Runs a specified plugin effect");
1306 pluginRunCommand.AddArgument("name", "The plugin effect you wish to run, or 'list' to see all plugins", "String"); 1661 pluginRunCommand.AddArgument("name", "The plugin effect you wish to run, or 'list' to see all plugins", "String");
@@ -1316,6 +1671,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1316 m_commander.RegisterCommand("bake", bakeRegionCommand); 1671 m_commander.RegisterCommand("bake", bakeRegionCommand);
1317 m_commander.RegisterCommand("revert", revertRegionCommand); 1672 m_commander.RegisterCommand("revert", revertRegionCommand);
1318 m_commander.RegisterCommand("newbrushes", experimentalBrushesCommand); 1673 m_commander.RegisterCommand("newbrushes", experimentalBrushesCommand);
1674 m_commander.RegisterCommand("show", showCommand);
1319 m_commander.RegisterCommand("stats", showDebugStatsCommand); 1675 m_commander.RegisterCommand("stats", showDebugStatsCommand);
1320 m_commander.RegisterCommand("effect", pluginRunCommand); 1676 m_commander.RegisterCommand("effect", pluginRunCommand);
1321 m_commander.RegisterCommand("flip", flipCommand); 1677 m_commander.RegisterCommand("flip", flipCommand);
@@ -1325,10 +1681,66 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1325 1681
1326 // Add this to our scene so scripts can call these functions 1682 // Add this to our scene so scripts can call these functions
1327 m_scene.RegisterModuleCommander(m_commander); 1683 m_scene.RegisterModuleCommander(m_commander);
1684
1685 // Add Modify command to Scene, since Command object requires fixed-length arglists
1686 m_scene.AddCommand("Terrain", this, "terrain modify",
1687 "terrain modify <operation> <value> [<area>] [<taper>]",
1688 "Modifies the terrain as instructed." +
1689 "\nEach operation can be limited to an area of effect:" +
1690 "\n * -ell=x,y,rx[,ry] constrains the operation to an ellipse centred at x,y" +
1691 "\n * -rec=x,y,dx[,dy] constrains the operation to a rectangle based at x,y" +
1692 "\nEach operation can have its effect tapered based on distance from centre:" +
1693 "\n * elliptical operations taper as cones" +
1694 "\n * rectangular operations taper as pyramids"
1695 ,
1696 ModifyCommand);
1697
1328 } 1698 }
1329 1699
1700 public void ModifyCommand(string module, string[] cmd)
1701 {
1702 string result;
1703 Scene scene = SceneManager.Instance.CurrentScene;
1704 if ((scene != null) && (scene != m_scene))
1705 {
1706 result = String.Empty;
1707 }
1708 else if (cmd.Length > 2)
1709 {
1710 string operationType = cmd[2];
1330 1711
1331 #endregion 1712
1713 ITerrainModifier operation;
1714 if (!m_modifyOperations.TryGetValue(operationType, out operation))
1715 {
1716 result = String.Format("Terrain Modify \"{0}\" not found.", operationType);
1717 }
1718 else if ((cmd.Length > 3) && (cmd[3] == "usage"))
1719 {
1720 result = "Usage: " + operation.GetUsage();
1721 }
1722 else
1723 {
1724 result = operation.ModifyTerrain(m_channel, cmd);
1725 }
1726
1727 if (result == String.Empty)
1728 {
1729 result = "Modified terrain";
1730 m_log.DebugFormat("Performed terrain operation {0}", operationType);
1731 }
1732 }
1733 else
1734 {
1735 result = "Usage: <operation-name> <arg1> <arg2>...";
1736 }
1737 if (result != String.Empty)
1738 {
1739 MainConsole.Instance.Output(result);
1740 }
1741 }
1742
1743#endregion
1332 1744
1333 } 1745 }
1334} 1746}