aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules
diff options
context:
space:
mode:
authorUbitUmarov2017-07-20 11:30:12 +0100
committerUbitUmarov2017-07-20 11:30:12 +0100
commitfe6ad384e43c11c3bf6268f5ab6fe3ea37c74540 (patch)
tree44ae3d8b00f3d2f6bf78bc0226a779f99ed6dc48 /OpenSim/Region/OptionalModules
parentMerge branch 'master' into httptests (diff)
parentfix object updates throttle for scripts doing motion by direct change of posi... (diff)
downloadopensim-SC-fe6ad384e43c11c3bf6268f5ab6fe3ea37c74540.zip
opensim-SC-fe6ad384e43c11c3bf6268f5ab6fe3ea37c74540.tar.gz
opensim-SC-fe6ad384e43c11c3bf6268f5ab6fe3ea37c74540.tar.bz2
opensim-SC-fe6ad384e43c11c3bf6268f5ab6fe3ea37c74540.tar.xz
merge
Diffstat (limited to 'OpenSim/Region/OptionalModules')
-rw-r--r--OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs785
-rw-r--r--OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs68
-rw-r--r--OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs710
3 files changed, 669 insertions, 894 deletions
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 79b80f8..a14d819 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -59,23 +59,23 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
59 /// </summary> 59 /// </summary>
60 /// <remarks> 60 /// <remarks>
61 /// Config Settings Documentation. 61 /// Config Settings Documentation.
62 /// Each configuration setting can be specified in two places: OpenSim.ini or Regions.ini. 62 /// Configuration setting can be specified in two places: OpenSim.ini and/or Regions.ini.
63 /// If specified in Regions.ini, the settings should be within the region's section name.
64 /// If specified in OpenSim.ini, the settings should be within the [AutoBackupModule] section.
65 /// Region-specific settings take precedence.
66 /// 63 ///
67 /// AutoBackupModuleEnabled: True/False. Default: False. If True, use the auto backup module. This setting does not support per-region basis. 64 /// OpenSim.ini only settings section [AutoBackupModule]
68 /// All other settings under [AutoBackupModule] are ignored if AutoBackupModuleEnabled is false, even per-region settings! 65 /// AutoBackupModuleEnabled: True/False. Default: False. If True, use the auto backup module.
69 /// AutoBackup: True/False. Default: False. If True, activate auto backup functionality. 66 /// if false module is disable and all rest is ignored
70 /// This is the only required option for enabling auto-backup; the other options have sane defaults.
71 /// If False for a particular region, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
72 /// If False globally (the default), only regions that specifically override it in Regions.ini will get AutoBackup functionality.
73 /// AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours). 67 /// AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours).
74 /// The number of minutes between each backup attempt. 68 /// The number of minutes between each backup attempt.
75 /// If a negative or zero value is given, it is equivalent to setting AutoBackup = False. 69 /// AutoBackupDir: String. Default: "." (the current directory).
76 /// AutoBackupBusyCheck: True/False. Default: True. 70 /// A directory (absolute or relative) where backups should be saved.
77 /// If True, we will only take an auto-backup if a set of conditions are met. 71 /// AutoBackupKeepFilesForDays remove files older than this number of days. 0 disables
78 /// These conditions are heuristics to try and avoid taking a backup when the sim is busy. 72 ///
73 /// Next can be set on OpenSim.ini, as default, and or per region in Regions.ini
74 /// Region-specific settings take precedence.
75 ///
76 /// AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
77 /// controls backup per region, with default optionaly set on OpenSim.ini
78
79 /// AutoBackupSkipAssets 79 /// AutoBackupSkipAssets
80 /// If true, assets are not saved to the oar file. Considerably reduces impact on simulator when backing up. Intended for when assets db is backed up separately 80 /// If true, assets are not saved to the oar file. Considerably reduces impact on simulator when backing up. Intended for when assets db is backed up separately
81 /// AutoBackupKeepFilesForDays 81 /// AutoBackupKeepFilesForDays
@@ -89,40 +89,28 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
89 /// AutoBackupNaming: string. Default: Time. 89 /// AutoBackupNaming: string. Default: Time.
90 /// One of three strings (case insensitive): 90 /// One of three strings (case insensitive):
91 /// "Time": Current timestamp is appended to file name. An existing file will never be overwritten. 91 /// "Time": Current timestamp is appended to file name. An existing file will never be overwritten.
92 /// "Sequential": A number is appended to the file name. So if RegionName_x.oar exists, we'll save to RegionName_{x+1}.oar next. An existing file will never be overwritten. 92 /// "Sequential": A number is appended to the file name. So if RegionName_x.oar exists, we'll save to RegionName_{x+1}.oar next. An existing file will never be overwritten.
93 /// "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file. 93 /// "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file.
94 /// AutoBackupDir: String. Default: "." (the current directory).
95 /// A directory (absolute or relative) where backups should be saved.
96 /// AutoBackupDilationThreshold: float. Default: 0.5. Lower bound on time dilation required for BusyCheck heuristics to pass.
97 /// If the time dilation is below this value, don't take a backup right now.
98 /// AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass.
99 /// If the number of agents is greater than this value, don't take a backup right now
100 /// Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
101 /// Also helps if you don't want AutoBackup at all.
102 /// </remarks> 94 /// </remarks>
103 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AutoBackupModule")] 95 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AutoBackupModule")]
104 public class AutoBackupModule : ISharedRegionModule 96 public class AutoBackupModule : ISharedRegionModule
105 { 97 {
106 private static readonly ILog m_log = 98 private static readonly ILog m_log =
107 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 99 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
108 private readonly Dictionary<Guid, IScene> m_pendingSaves = new Dictionary<Guid, IScene>(1);
109 private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState(); 100 private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState();
110 private readonly Dictionary<IScene, AutoBackupModuleState> m_states = 101 private readonly Dictionary<IScene, AutoBackupModuleState> m_states =
111 new Dictionary<IScene, AutoBackupModuleState>(1); 102 new Dictionary<IScene, AutoBackupModuleState>(1);
112 private readonly Dictionary<Timer, List<IScene>> m_timerMap =
113 new Dictionary<Timer, List<IScene>>(1);
114 private readonly Dictionary<double, Timer> m_timers = new Dictionary<double, Timer>(1);
115 103
116 private delegate T DefaultGetter<T>(string settingName, T defaultValue); 104 private delegate T DefaultGetter<T>(string settingName, T defaultValue);
117 private bool m_enabled; 105 private bool m_enabled;
118 private ICommandConsole m_console; 106 private ICommandConsole m_console;
119 private List<Scene> m_Scenes = new List<Scene> (); 107 private List<Scene> m_Scenes = new List<Scene> ();
120 108 private Timer m_masterTimer;
121 109 private bool m_busy;
122 /// <summary> 110 private int m_KeepFilesForDays = -1;
123 /// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState! 111 private string m_backupDir;
124 /// </summary> 112 private bool m_doneFirst;
125 private bool m_closed; 113 private double m_baseInterval;
126 114
127 private IConfigSource m_configSource; 115 private IConfigSource m_configSource;
128 116
@@ -159,36 +147,38 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
159 void IRegionModuleBase.Initialise(IConfigSource source) 147 void IRegionModuleBase.Initialise(IConfigSource source)
160 { 148 {
161 // Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module 149 // Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
162 this.m_configSource = source; 150 m_configSource = source;
163 IConfig moduleConfig = source.Configs["AutoBackupModule"]; 151 IConfig moduleConfig = source.Configs["AutoBackupModule"];
164 if (moduleConfig == null) 152 if (moduleConfig == null)
165 { 153 {
166 this.m_enabled = false; 154 m_enabled = false;
167 return; 155 return;
168 } 156 }
169 else
170 {
171 this.m_enabled = moduleConfig.GetBoolean("AutoBackupModuleEnabled", false);
172 if (this.m_enabled)
173 {
174 m_log.Info("[AUTO BACKUP]: AutoBackupModule enabled");
175 }
176 else
177 {
178 return;
179 }
180 }
181 157
182 Timer defTimer = new Timer(43200000); 158 m_enabled = moduleConfig.GetBoolean("AutoBackupModuleEnabled", false);
183 this.m_defaultState.Timer = defTimer; 159 if(!m_enabled)
184 this.m_timers.Add(43200000, defTimer); 160 return;
185 defTimer.Elapsed += this.HandleElapsed; 161
186 defTimer.AutoReset = true; 162 ParseDefaultConfig(moduleConfig);
187 defTimer.Start(); 163 if(!m_enabled)
164 return;
165
166 m_log.Debug("[AUTO BACKUP]: Default config:");
167 m_log.Debug(m_defaultState.ToString());
168
169 m_log.Info("[AUTO BACKUP]: AutoBackupModule enabled");
170 m_masterTimer = new Timer();
171 m_masterTimer.Interval = m_baseInterval;
172 m_masterTimer.Elapsed += HandleElapsed;
173 m_masterTimer.AutoReset = false;
188 174
189 AutoBackupModuleState abms = this.ParseConfig(null, true); 175 m_console = MainConsole.Instance;
190 m_log.Debug("[AUTO BACKUP]: Here is the default config:"); 176
191 m_log.Debug(abms.ToString()); 177 m_console.Commands.AddCommand (
178 "AutoBackup", true, "dooarbackup",
179 "dooarbackup <regionName> | ALL",
180 "saves the single region <regionName> to a oar or ALL regions in instance to oars, using same settings as AutoBackup. Note it restarts time interval", DoBackup);
181 m_busy = true;
192 } 182 }
193 183
194 /// <summary> 184 /// <summary>
@@ -196,13 +186,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
196 /// </summary> 186 /// </summary>
197 void IRegionModuleBase.Close() 187 void IRegionModuleBase.Close()
198 { 188 {
199 if (!this.m_enabled) 189 if (!m_enabled)
200 {
201 return; 190 return;
202 }
203 191
204 // We don't want any timers firing while the sim's coming down; strange things may happen. 192 // We don't want any timers firing while the sim's coming down; strange things may happen.
205 this.StopAllTimers(); 193 m_masterTimer.Dispose();
206 } 194 }
207 195
208 /// <summary> 196 /// <summary>
@@ -211,18 +199,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
211 /// <param name="scene"></param> 199 /// <param name="scene"></param>
212 void IRegionModuleBase.AddRegion (Scene scene) 200 void IRegionModuleBase.AddRegion (Scene scene)
213 { 201 {
214 if (!this.m_enabled) { 202 if (!m_enabled)
215 return; 203 return;
216 }
217 lock (m_Scenes) {
218 m_Scenes.Add (scene);
219 }
220 m_console = MainConsole.Instance;
221 204
222 m_console.Commands.AddCommand ( 205 lock (m_Scenes)
223 "AutoBackup", false, "dobackup", 206 m_Scenes.Add (scene);
224 "dobackup",
225 "do backup.", DoBackup);
226 } 207 }
227 208
228 /// <summary> 209 /// <summary>
@@ -231,28 +212,14 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
231 /// <param name="scene">The scene (region) to stop performing AutoBackup on.</param> 212 /// <param name="scene">The scene (region) to stop performing AutoBackup on.</param>
232 void IRegionModuleBase.RemoveRegion(Scene scene) 213 void IRegionModuleBase.RemoveRegion(Scene scene)
233 { 214 {
234 if (!this.m_enabled) 215 if (m_enabled)
235 {
236 return; 216 return;
237 }
238 m_Scenes.Remove (scene);
239 if (this.m_states.ContainsKey(scene))
240 {
241 AutoBackupModuleState abms = this.m_states[scene];
242 217
243 // Remove this scene out of the timer map list 218 lock(m_Scenes)
244 Timer timer = abms.Timer; 219 {
245 List<IScene> list = this.m_timerMap[timer]; 220 if (m_states.ContainsKey(scene))
246 list.Remove(scene); 221 m_states.Remove(scene);
247 222 m_Scenes.Remove(scene);
248 // Shut down the timer if this was the last scene for the timer
249 if (list.Count == 0)
250 {
251 this.m_timerMap.Remove(timer);
252 this.m_timers.Remove(timer.Interval);
253 timer.Close();
254 }
255 this.m_states.Remove(scene);
256 } 223 }
257 } 224 }
258 225
@@ -263,22 +230,29 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
263 /// <param name="scene">The scene to (possibly) perform AutoBackup on.</param> 230 /// <param name="scene">The scene to (possibly) perform AutoBackup on.</param>
264 void IRegionModuleBase.RegionLoaded(Scene scene) 231 void IRegionModuleBase.RegionLoaded(Scene scene)
265 { 232 {
266 if (!this.m_enabled) 233 if (!m_enabled)
267 {
268 return; 234 return;
269 }
270 235
271 // This really ought not to happen, but just in case, let's pretend it didn't... 236 // This really ought not to happen, but just in case, let's pretend it didn't...
272 if (scene == null) 237 if (scene == null)
273 {
274 return; 238 return;
275 }
276 239
277 AutoBackupModuleState abms = this.ParseConfig(scene, false); 240 AutoBackupModuleState abms = ParseConfig(scene);
278 m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName); 241 if(abms == null)
279 m_log.Debug((abms == null ? "DEFAULT" : abms.ToString())); 242 {
243 m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
244 m_log.Debug("DEFAULT");
245 abms = new AutoBackupModuleState(m_defaultState);
246 }
247 else
248 {
249 m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
250 m_log.Debug(abms.ToString());
251 }
280 252
281 m_states.Add(scene, abms); 253 m_states.Add(scene, abms);
254 m_busy = false;
255 m_masterTimer.Start();
282 } 256 }
283 257
284 /// <summary> 258 /// <summary>
@@ -292,356 +266,174 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
292 266
293 private void DoBackup (string module, string[] args) 267 private void DoBackup (string module, string[] args)
294 { 268 {
295 if (args.Length != 2) { 269 if (!m_enabled)
296 MainConsole.Instance.OutputFormat ("Usage: dobackup <regionname>");
297 return; 270 return;
298 }
299 bool found = false;
300 string name = args [1];
301 lock (m_Scenes) {
302 foreach (Scene s in m_Scenes) {
303 string test = s.Name.ToString ();
304 if (test == name) {
305 found = true;
306 DoRegionBackup (s);
307 }
308 }
309 if (!found) {
310 MainConsole.Instance.OutputFormat ("No such region {0}. Nothing to backup", name);
311 }
312 }
313 }
314 271
315 /// <summary> 272 if (args.Length != 2)
316 /// Set up internal state for a given scene. Fairly complex code.
317 /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
318 /// </summary>
319 /// <param name="scene">The scene to look at.</param>
320 /// <param name="parseDefault">Whether this call is intended to figure out what we consider the "default" config (applied to all regions unless overridden by per-region settings).</param>
321 /// <returns>An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.</returns>
322 private AutoBackupModuleState ParseConfig(IScene scene, bool parseDefault)
323 {
324 string sRegionName;
325 string sRegionLabel;
326// string prepend;
327 AutoBackupModuleState state;
328
329 if (parseDefault)
330 { 273 {
331 sRegionName = null; 274 MainConsole.Instance.OutputFormat ("Usage: dooarbackup <regionname>");
332 sRegionLabel = "DEFAULT"; 275 return;
333// prepend = "";
334 state = this.m_defaultState;
335 }
336 else
337 {
338 sRegionName = scene.RegionInfo.RegionName;
339 sRegionLabel = sRegionName;
340// prepend = sRegionName + ".";
341 state = null;
342 }
343
344 // Read the config settings and set variables.
345 IConfig regionConfig = (scene != null ? scene.Config.Configs[sRegionName] : null);
346 IConfig config = this.m_configSource.Configs["AutoBackupModule"];
347 if (config == null)
348 {
349 // defaultState would be disabled too if the section doesn't exist.
350 state = this.m_defaultState;
351 return state;
352 } 276 }
353 277
354 bool tmpEnabled = ResolveBoolean("AutoBackup", this.m_defaultState.Enabled, config, regionConfig); 278 if(m_busy)
355 if (state == null && tmpEnabled != this.m_defaultState.Enabled)
356 //Varies from default state
357 { 279 {
358 state = new AutoBackupModuleState(); 280 MainConsole.Instance.OutputFormat ("Already doing a backup, please try later");
281 return;
359 } 282 }
360 283
361 if (state != null) 284 m_masterTimer.Stop();
362 { 285 m_busy = true;
363 state.Enabled = tmpEnabled;
364 }
365 286
366 // If you don't want AutoBackup, we stop. 287 bool found = false;
367 if ((state == null && !this.m_defaultState.Enabled) || (state != null && !state.Enabled)) 288 string name = args [1];
368 { 289 Scene[] scenes;
369 return state; 290 lock (m_Scenes)
370 } 291 scenes = m_Scenes.ToArray();
371 else
372 {
373 m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED.");
374 }
375
376 // Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
377 double interval =
378 this.ResolveDouble("AutoBackupInterval", this.m_defaultState.IntervalMinutes,
379 config, regionConfig) * 60000.0;
380 if (state == null && interval != this.m_defaultState.IntervalMinutes * 60000.0)
381 {
382 state = new AutoBackupModuleState();
383 }
384 292
385 if (this.m_timers.ContainsKey(interval)) 293 if(scenes == null)
386 { 294 return;
387 if (state != null)
388 {
389 state.Timer = this.m_timers[interval];
390 }
391 m_log.Debug("[AUTO BACKUP]: Reusing timer for " + interval + " msec for region " +
392 sRegionLabel);
393 }
394 else
395 {
396 // 0 or negative interval == do nothing.
397 if (interval <= 0.0 && state != null)
398 {
399 state.Enabled = false;
400 return state;
401 }
402 if (state == null)
403 {
404 state = new AutoBackupModuleState();
405 }
406 Timer tim = new Timer(interval);
407 state.Timer = tim;
408 //Milliseconds -> minutes
409 this.m_timers.Add(interval, tim);
410 tim.Elapsed += this.HandleElapsed;
411 tim.AutoReset = true;
412 tim.Start();
413 }
414 295
415 // Add the current region to the list of regions tied to this timer. 296 Scene s;
416 if (scene != null) 297 try
417 { 298 {
418 if (state != null) 299 if(name == "ALL")
419 { 300 {
420 if (this.m_timerMap.ContainsKey(state.Timer)) 301 for(int i = 0; i < scenes.Length; i++)
421 { 302 {
422 this.m_timerMap[state.Timer].Add(scene); 303 s = scenes[i];
423 } 304 DoRegionBackup(s);
424 else 305 if (!m_enabled)
425 { 306 return;
426 List<IScene> scns = new List<IScene>(1);
427 scns.Add(scene);
428 this.m_timerMap.Add(state.Timer, scns);
429 } 307 }
308 return;
430 } 309 }
431 else 310
311 for(int i = 0; i < scenes.Length; i++)
432 { 312 {
433 if (this.m_timerMap.ContainsKey(this.m_defaultState.Timer)) 313 s = scenes[i];
434 { 314 if (s.Name == name)
435 this.m_timerMap[this.m_defaultState.Timer].Add(scene);
436 }
437 else
438 { 315 {
439 List<IScene> scns = new List<IScene>(1); 316 found = true;
440 scns.Add(scene); 317 DoRegionBackup(s);
441 this.m_timerMap.Add(this.m_defaultState.Timer, scns); 318 break;
442 } 319 }
443 } 320 }
444 } 321 }
445 322 catch { }
446 bool tmpBusyCheck = ResolveBoolean("AutoBackupBusyCheck", 323 finally
447 this.m_defaultState.BusyCheck, config, regionConfig);
448 if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck)
449 { 324 {
450 state = new AutoBackupModuleState(); 325 if (m_enabled)
451 } 326 m_masterTimer.Start();
327 m_busy = false;
328 }
329 if (!found)
330 MainConsole.Instance.OutputFormat ("No such region {0}. Nothing to backup", name);
331 }
452 332
453 if (state != null) 333 private void ParseDefaultConfig(IConfig config)
454 { 334 {
455 state.BusyCheck = tmpBusyCheck;
456 }
457 335
458 // Included Option To Skip Assets 336 m_backupDir = ".";
459 bool tmpSkipAssets = ResolveBoolean("AutoBackupSkipAssets", 337 string backupDir = config.GetString("AutoBackupDir", ".");
460 this.m_defaultState.SkipAssets, config, regionConfig); 338 if (backupDir != ".")
461 if (state == null && tmpSkipAssets != this.m_defaultState.SkipAssets)
462 { 339 {
463 state = new AutoBackupModuleState(); 340 try
341 {
342 DirectoryInfo dirinfo = new DirectoryInfo(backupDir);
343 if (!dirinfo.Exists)
344 dirinfo.Create();
345 }
346 catch (Exception e)
347 {
348 m_enabled = false;
349 m_log.WarnFormat("[AUTO BACKUP]: Error accessing backup folder {0}. Module disabled. {1}",
350 backupDir, e);
351 return;
352 }
464 } 353 }
354 m_backupDir = backupDir;
465 355
466 if (state != null) 356 double interval = config.GetDouble("AutoBackupInterval", 720);
467 { 357 interval *= 60000.0;
468 state.SkipAssets = tmpSkipAssets; 358 m_baseInterval = interval;
469 }
470 359
471 // How long to keep backup files in days, 0 Disables this feature 360 // How long to keep backup files in days, 0 Disables this feature
472 int tmpKeepFilesForDays = ResolveInt("AutoBackupKeepFilesForDays", 361 m_KeepFilesForDays = config.GetInt("AutoBackupKeepFilesForDays",m_KeepFilesForDays);
473 this.m_defaultState.KeepFilesForDays, config, regionConfig);
474 if (state == null && tmpKeepFilesForDays != this.m_defaultState.KeepFilesForDays)
475 {
476 state = new AutoBackupModuleState();
477 }
478 362
479 if (state != null) 363 m_defaultState.Enabled = config.GetBoolean("AutoBackup", m_defaultState.Enabled);
480 { 364
481 state.KeepFilesForDays = tmpKeepFilesForDays; 365 m_defaultState.SkipAssets = config.GetBoolean("AutoBackupSkipAssets",m_defaultState.SkipAssets);
482 }
483 366
484 // Set file naming algorithm 367 // Set file naming algorithm
485 string stmpNamingType = ResolveString("AutoBackupNaming", 368 string stmpNamingType = config.GetString("AutoBackupNaming", m_defaultState.NamingType.ToString());
486 this.m_defaultState.NamingType.ToString(), config, regionConfig);
487 NamingType tmpNamingType; 369 NamingType tmpNamingType;
488 if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase)) 370 if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
489 {
490 tmpNamingType = NamingType.Time; 371 tmpNamingType = NamingType.Time;
491 }
492 else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase)) 372 else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
493 {
494 tmpNamingType = NamingType.Sequential; 373 tmpNamingType = NamingType.Sequential;
495 }
496 else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase)) 374 else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
497 {
498 tmpNamingType = NamingType.Overwrite; 375 tmpNamingType = NamingType.Overwrite;
499 }
500 else 376 else
501 { 377 {
502 m_log.Warn("Unknown naming type specified for region " + sRegionLabel + ": " + 378 m_log.Warn("Unknown naming type specified for Default");
503 stmpNamingType);
504 tmpNamingType = NamingType.Time; 379 tmpNamingType = NamingType.Time;
505 } 380 }
381 m_defaultState.NamingType = tmpNamingType;
506 382
507 if (state == null && tmpNamingType != this.m_defaultState.NamingType) 383 m_defaultState.Script = config.GetString("AutoBackupScript", m_defaultState.Script);
508 {
509 state = new AutoBackupModuleState();
510 }
511 384
512 if (state != null) 385 }
513 {
514 state.NamingType = tmpNamingType;
515 }
516 386
517 string tmpScript = ResolveString("AutoBackupScript", 387 /// <summary>
518 this.m_defaultState.Script, config, regionConfig); 388 /// Set up internal state for a given scene. Fairly complex code.
519 if (state == null && tmpScript != this.m_defaultState.Script) 389 /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
520 { 390 /// </summary>
521 state = new AutoBackupModuleState(); 391 /// <param name="scene">The scene to look at.</param>
522 } 392 /// <param name="parseDefault">Whether this call is intended to figure out what we consider the "default" config (applied to all regions unless overridden by per-region settings).</param>
393 /// <returns>An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.</returns>
394 private AutoBackupModuleState ParseConfig(IScene scene)
395 {
396 if(scene == null)
397 return null;
523 398
524 if (state != null) 399 string sRegionName;
525 { 400 AutoBackupModuleState state = null;
526 state.Script = tmpScript;
527 }
528 401
529 string tmpBackupDir = ResolveString("AutoBackupDir", ".", config, regionConfig); 402 sRegionName = scene.RegionInfo.RegionName;
530 if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
531 {
532 state = new AutoBackupModuleState();
533 }
534 403
535 if (state != null) 404 // Read the config settings and set variables.
536 { 405 IConfig regionConfig = scene.Config.Configs[sRegionName];
537 state.BackupDir = tmpBackupDir; 406 if (regionConfig == null)
538 // Let's give the user some convenience and auto-mkdir 407 return null;
539 if (state.BackupDir != ".")
540 {
541 try
542 {
543 DirectoryInfo dirinfo = new DirectoryInfo(state.BackupDir);
544 if (!dirinfo.Exists)
545 {
546 dirinfo.Create();
547 }
548 }
549 catch (Exception e)
550 {
551 m_log.Warn(
552 "[AUTO BACKUP]: BAD NEWS. You won't be able to save backups to directory " +
553 state.BackupDir +
554 " because it doesn't exist or there's a permissions issue with it. Here's the exception.",
555 e);
556 }
557 }
558 }
559 408
560 if(state == null) 409 state = new AutoBackupModuleState();
561 return m_defaultState;
562 410
563 return state; 411 state.Enabled = regionConfig.GetBoolean("AutoBackup", m_defaultState.Enabled);
564 }
565 412
566 /// <summary> 413 // Included Option To Skip Assets
567 /// Helper function for ParseConfig. 414 state.SkipAssets = regionConfig.GetBoolean("AutoBackupSkipAssets", m_defaultState.SkipAssets);
568 /// </summary>
569 /// <param name="settingName"></param>
570 /// <param name="defaultValue"></param>
571 /// <param name="global"></param>
572 /// <param name="local"></param>
573 /// <returns></returns>
574 private bool ResolveBoolean(string settingName, bool defaultValue, IConfig global, IConfig local)
575 {
576 if(local != null)
577 {
578 return local.GetBoolean(settingName, global.GetBoolean(settingName, defaultValue));
579 }
580 else
581 {
582 return global.GetBoolean(settingName, defaultValue);
583 }
584 }
585 415
586 /// <summary> 416 // Set file naming algorithm
587 /// Helper function for ParseConfig. 417 string stmpNamingType = regionConfig.GetString("AutoBackupNaming", m_defaultState.NamingType.ToString());
588 /// </summary> 418 NamingType tmpNamingType;
589 /// <param name="settingName"></param> 419 if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
590 /// <param name="defaultValue"></param> 420 tmpNamingType = NamingType.Time;
591 /// <param name="global"></param> 421 else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
592 /// <param name="local"></param> 422 tmpNamingType = NamingType.Sequential;
593 /// <returns></returns> 423 else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
594 private double ResolveDouble(string settingName, double defaultValue, IConfig global, IConfig local) 424 tmpNamingType = NamingType.Overwrite;
595 {
596 if (local != null)
597 {
598 return local.GetDouble(settingName, global.GetDouble(settingName, defaultValue));
599 }
600 else 425 else
601 { 426 {
602 return global.GetDouble(settingName, defaultValue); 427 m_log.Warn("Unknown naming type specified for region " + sRegionName + ": " +
428 stmpNamingType);
429 tmpNamingType = NamingType.Time;
603 } 430 }
604 } 431 m_defaultState.NamingType = tmpNamingType;
605 432
606 /// <summary> 433 state.Script = regionConfig.GetString("AutoBackupScript", m_defaultState.Script);
607 /// Helper function for ParseConfig. 434 return state;
608 /// </summary>
609 /// <param name="settingName"></param>
610 /// <param name="defaultValue"></param>
611 /// <param name="global"></param>
612 /// <param name="local"></param>
613 /// <returns></returns>
614 private int ResolveInt(string settingName, int defaultValue, IConfig global, IConfig local)
615 {
616 if (local != null)
617 {
618 return local.GetInt(settingName, global.GetInt(settingName, defaultValue));
619 }
620 else
621 {
622 return global.GetInt(settingName, defaultValue);
623 }
624 } 435 }
625 436
626 /// <summary>
627 /// Helper function for ParseConfig.
628 /// </summary>
629 /// <param name="settingName"></param>
630 /// <param name="defaultValue"></param>
631 /// <param name="global"></param>
632 /// <param name="local"></param>
633 /// <returns></returns>
634 private string ResolveString(string settingName, string defaultValue, IConfig global, IConfig local)
635 {
636 if (local != null)
637 {
638 return local.GetString(settingName, global.GetString(settingName, defaultValue));
639 }
640 else
641 {
642 return global.GetString(settingName, defaultValue);
643 }
644 }
645 437
646 /// <summary> 438 /// <summary>
647 /// Called when any auto-backup timer expires. This starts the code path for actually performing a backup. 439 /// Called when any auto-backup timer expires. This starts the code path for actually performing a backup.
@@ -650,63 +442,27 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
650 /// <param name="e"></param> 442 /// <param name="e"></param>
651 private void HandleElapsed(object sender, ElapsedEventArgs e) 443 private void HandleElapsed(object sender, ElapsedEventArgs e)
652 { 444 {
653 // TODO: heuristic thresholds are per-region, so we should probably run heuristics once per region 445 if (!m_enabled || m_busy)
654 // XXX: Running heuristics once per region could add undue performance penalty for something that's supposed to
655 // check whether the region is too busy! Especially on sims with LOTS of regions.
656 // Alternative: make heuristics thresholds global to the module rather than per-region. Less flexible,
657 // but would allow us to be semantically correct while being easier on perf.
658 // Alternative 2: Run heuristics once per unique set of heuristics threshold parameters! Ay yi yi...
659 // Alternative 3: Don't support per-region heuristics at all; just accept them as a global only parameter.
660 // Since this is pretty experimental, I haven't decided which alternative makes the most sense.
661 if (this.m_closed)
662 {
663 return; 446 return;
664 } 447
665 bool heuristicsRun = false; 448 m_busy = true;
666 bool heuristicsPassed = false; 449 if(m_doneFirst && m_KeepFilesForDays > 0)
667 if (!this.m_timerMap.ContainsKey((Timer) sender)) 450 RemoveOldFiles();
451
452 foreach (IScene scene in m_Scenes)
668 { 453 {
669 m_log.Debug("[AUTO BACKUP]: Code-up error: timerMap doesn't contain timer " + sender); 454 if (!m_enabled)
455 return;
456 DoRegionBackup(scene);
670 } 457 }
671 458
672 List<IScene> tmap = this.m_timerMap[(Timer) sender]; 459 if (m_enabled)
673 if (tmap != null && tmap.Count > 0)
674 { 460 {
675 foreach (IScene scene in tmap) 461 m_masterTimer.Start();
676 { 462 m_busy = false;
677 AutoBackupModuleState state = this.m_states[scene];
678 bool heuristics = state.BusyCheck;
679
680 // Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
681 if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics)
682 {
683 this.DoRegionBackup(scene);
684 // Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
685 }
686 else if (heuristicsRun)
687 {
688 m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
689 scene.RegionInfo.RegionName + " right now.");
690 continue;
691 // Logical Deduction: heuristics are on but haven't been run
692 }
693 else
694 {
695 heuristicsPassed = this.RunHeuristics(scene);
696 heuristicsRun = true;
697 if (!heuristicsPassed)
698 {
699 m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
700 scene.RegionInfo.RegionName + " right now.");
701 continue;
702 }
703 this.DoRegionBackup(scene);
704 }
705
706 // Remove Old Backups
707 this.RemoveOldFiles(state);
708 }
709 } 463 }
464
465 m_doneFirst = true;
710 } 466 }
711 467
712 /// <summary> 468 /// <summary>
@@ -723,21 +479,29 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
723 return; 479 return;
724 } 480 }
725 481
726 AutoBackupModuleState state = this.m_states[scene]; 482 m_busy = true;
483
484 AutoBackupModuleState state;
485 if(!m_states.TryGetValue(scene, out state))
486 return;
487
488 if(state == null || !state.Enabled)
489 return;
490
727 IRegionArchiverModule iram = scene.RequestModuleInterface<IRegionArchiverModule>(); 491 IRegionArchiverModule iram = scene.RequestModuleInterface<IRegionArchiverModule>();
492 if(iram == null)
493 return;
494
728 string savePath = BuildOarPath(scene.RegionInfo.RegionName, 495 string savePath = BuildOarPath(scene.RegionInfo.RegionName,
729 state.BackupDir, 496 m_backupDir,
730 state.NamingType); 497 state.NamingType);
731 if (savePath == null) 498 if (savePath == null)
732 { 499 {
733 m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed"); 500 m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed");
734 return; 501 return;
735 } 502 }
736 Guid guid = Guid.NewGuid();
737 m_pendingSaves.Add(guid, scene);
738 state.LiveRequests.Add(guid, savePath);
739 ((Scene) scene).EventManager.OnOarFileSaved += new EventManager.OarFileSaved(EventManager_OnOarFileSaved);
740 503
504 Guid guid = Guid.NewGuid();
741 m_log.Info("[AUTO BACKUP]: Backing up region " + scene.RegionInfo.RegionName); 505 m_log.Info("[AUTO BACKUP]: Backing up region " + scene.RegionInfo.RegionName);
742 506
743 // Must pass options, even if dictionary is empty! 507 // Must pass options, even if dictionary is empty!
@@ -747,47 +511,37 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
747 options["noassets"] = true; 511 options["noassets"] = true;
748 512
749 iram.ArchiveRegion(savePath, guid, options); 513 iram.ArchiveRegion(savePath, guid, options);
514 ExecuteScript(state.Script, savePath);
750 } 515 }
751 516
752 // For the given state, remove backup files older than the states KeepFilesForDays property 517 // For the given state, remove backup files older than the states KeepFilesForDays property
753 private void RemoveOldFiles(AutoBackupModuleState state) 518 private void RemoveOldFiles()
754 { 519 {
755 // 0 Means Disabled, Keep Files Indefinitely 520 string[] files;
756 if (state.KeepFilesForDays > 0) 521 try
757 { 522 {
758 string[] files = Directory.GetFiles(state.BackupDir, "*.oar"); 523 files = Directory.GetFiles(m_backupDir, "*.oar");
759 DateTime CuttOffDate = DateTime.Now.AddDays(0 - state.KeepFilesForDays); 524 }
760 525 catch (Exception Ex)
761 foreach (string file in files) 526 {
762 { 527 m_log.Error("[AUTO BACKUP]: Error reading backup folder " + m_backupDir + ": " + Ex.Message);
763 try 528 return;
764 {
765 FileInfo fi = new FileInfo(file);
766 if (fi.CreationTime < CuttOffDate)
767 fi.Delete();
768 }
769 catch (Exception Ex)
770 {
771 m_log.Error("[AUTO BACKUP]: Error deleting old backup file '" + file + "': " + Ex.Message);
772 }
773 }
774 } 529 }
775 }
776 530
777 /// <summary> 531 DateTime CuttOffDate = DateTime.Now.AddDays(-m_KeepFilesForDays);
778 /// Called by the Event Manager when the OnOarFileSaved event is fired. 532
779 /// </summary> 533 foreach (string file in files)
780 /// <param name="guid"></param>
781 /// <param name="message"></param>
782 void EventManager_OnOarFileSaved(Guid guid, string message)
783 {
784 // Ignore if the OAR save is being done by some other part of the system
785 if (m_pendingSaves.ContainsKey(guid))
786 { 534 {
787 AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])]; 535 try
788 ExecuteScript(abms.Script, abms.LiveRequests[guid]); 536 {
789 m_pendingSaves.Remove(guid); 537 FileInfo fi = new FileInfo(file);
790 abms.LiveRequests.Remove(guid); 538 if (fi.CreationTime < CuttOffDate)
539 fi.Delete();
540 }
541 catch (Exception Ex)
542 {
543 m_log.Error("[AUTO BACKUP]: Error deleting old backup file '" + file + "': " + Ex.Message);
544 }
791 } 545 }
792 } 546 }
793 547
@@ -817,63 +571,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
817 return output; 571 return output;
818 } 572 }
819 573
820 /// <summary>Return value of true ==> not too busy; false ==> too busy to backup an OAR right now, or error.</summary>
821 private bool RunHeuristics(IScene region)
822 {
823 try
824 {
825 return this.RunTimeDilationHeuristic(region) && this.RunAgentLimitHeuristic(region);
826 }
827 catch (Exception e)
828 {
829 m_log.Warn("[AUTO BACKUP]: Exception in RunHeuristics", e);
830 return false;
831 }
832 }
833
834 /// <summary>
835 /// If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5),
836 /// then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR).
837 /// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
838 /// </summary>
839 /// <param name="region"></param>
840 /// <returns>Returns true if we're not too busy; false means we've got worse time dilation than the threshold.</returns>
841 private bool RunTimeDilationHeuristic(IScene region)
842 {
843 string regionName = region.RegionInfo.RegionName;
844 return region.TimeDilation >=
845 this.m_configSource.Configs["AutoBackupModule"].GetFloat(
846 regionName + ".AutoBackupDilationThreshold", 0.5f);
847 }
848
849 /// <summary>
850 /// If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10),
851 /// then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR).
852 /// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
853 /// </summary>
854 /// <param name="region"></param>
855 /// <returns>Returns true if we're not too busy; false means we've got more agents on the sim than the threshold.</returns>
856 private bool RunAgentLimitHeuristic(IScene region)
857 {
858 string regionName = region.RegionInfo.RegionName;
859 try
860 {
861 Scene scene = (Scene) region;
862 // TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful...
863 return scene.GetRootAgentCount() <=
864 this.m_configSource.Configs["AutoBackupModule"].GetInt(
865 regionName + ".AutoBackupAgentThreshold", 10);
866 }
867 catch (InvalidCastException ice)
868 {
869 m_log.Debug(
870 "[AUTO BACKUP]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!",
871 ice);
872 return true;
873 // Non-obstructionist safest answer...
874 }
875 }
876
877 /// <summary> 574 /// <summary>
878 /// Run the script or executable specified by the "AutoBackupScript" config setting. 575 /// Run the script or executable specified by the "AutoBackupScript" config setting.
879 /// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script. 576 /// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script.
@@ -920,18 +617,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
920 } 617 }
921 618
922 /// <summary> 619 /// <summary>
923 /// Quickly stop all timers from firing.
924 /// </summary>
925 private void StopAllTimers()
926 {
927 foreach (Timer t in this.m_timerMap.Keys)
928 {
929 t.Close();
930 }
931 this.m_closed = true;
932 }
933
934 /// <summary>
935 /// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType. 620 /// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType.
936 /// </summary> 621 /// </summary>
937 /// <param name="dirName"></param> 622 /// <param name="dirName"></param>
@@ -1033,5 +718,3 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
1033 } 718 }
1034 } 719 }
1035} 720}
1036
1037
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
index b90f0c4..fb87677 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
@@ -38,26 +38,20 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
38 /// 38 ///
39 public class AutoBackupModuleState 39 public class AutoBackupModuleState
40 { 40 {
41 private Dictionary<Guid, string> m_liveRequests = null;
42
43 public AutoBackupModuleState() 41 public AutoBackupModuleState()
44 { 42 {
45 this.Enabled = false; 43 Enabled = false;
46 this.BackupDir = "."; 44 SkipAssets = false;
47 this.BusyCheck = true; 45 NamingType = NamingType.Time;
48 this.SkipAssets = false; 46 Script = null;
49 this.Timer = null;
50 this.NamingType = NamingType.Time;
51 this.Script = null;
52 this.KeepFilesForDays = 0;
53 } 47 }
54 48
55 public Dictionary<Guid, string> LiveRequests 49 public AutoBackupModuleState(AutoBackupModuleState copyFrom)
56 { 50 {
57 get { 51 Enabled = copyFrom.Enabled;
58 return this.m_liveRequests ?? 52 SkipAssets = copyFrom.SkipAssets;
59 (this.m_liveRequests = new Dictionary<Guid, string>(1)); 53 NamingType = copyFrom.NamingType;
60 } 54 Script = copyFrom.Script;
61 } 55 }
62 56
63 public bool Enabled 57 public bool Enabled
@@ -66,33 +60,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
66 set; 60 set;
67 } 61 }
68 62
69 public System.Timers.Timer Timer
70 {
71 get;
72 set;
73 }
74
75 public double IntervalMinutes
76 {
77 get
78 {
79 if (this.Timer == null)
80 {
81 return -1.0;
82 }
83 else
84 {
85 return this.Timer.Interval / 60000.0;
86 }
87 }
88 }
89
90 public bool BusyCheck
91 {
92 get;
93 set;
94 }
95
96 public bool SkipAssets 63 public bool SkipAssets
97 { 64 {
98 get; 65 get;
@@ -105,36 +72,19 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
105 set; 72 set;
106 } 73 }
107 74
108 public string BackupDir
109 {
110 get;
111 set;
112 }
113
114 public NamingType NamingType 75 public NamingType NamingType
115 { 76 {
116 get; 77 get;
117 set; 78 set;
118 } 79 }
119 80
120 public int KeepFilesForDays
121 {
122 get;
123 set;
124 }
125
126 public new string ToString() 81 public new string ToString()
127 { 82 {
128 string retval = ""; 83 string retval = "";
129
130 retval += "[AUTO BACKUP]: AutoBackup: " + (Enabled ? "ENABLED" : "DISABLED") + "\n"; 84 retval += "[AUTO BACKUP]: AutoBackup: " + (Enabled ? "ENABLED" : "DISABLED") + "\n";
131 retval += "[AUTO BACKUP]: Interval: " + IntervalMinutes + " minutes" + "\n";
132 retval += "[AUTO BACKUP]: Do Busy Check: " + (BusyCheck ? "Yes" : "No") + "\n";
133 retval += "[AUTO BACKUP]: Naming Type: " + NamingType.ToString() + "\n"; 85 retval += "[AUTO BACKUP]: Naming Type: " + NamingType.ToString() + "\n";
134 retval += "[AUTO BACKUP]: Backup Dir: " + BackupDir + "\n";
135 retval += "[AUTO BACKUP]: Script: " + Script + "\n"; 86 retval += "[AUTO BACKUP]: Script: " + Script + "\n";
136 return retval; 87 return retval;
137 } 88 }
138 } 89 }
139} 90}
140
diff --git a/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs b/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs
index da8c9a3..6e1f8bb 100644
--- a/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs
+++ b/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs
@@ -27,8 +27,13 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.IO;
30using System.Reflection; 31using System.Reflection;
31using System.Timers; 32using System.Timers;
33using System.Threading;
34using System.Xml;
35using System.Xml.Serialization;
36
32using OpenMetaverse; 37using OpenMetaverse;
33using log4net; 38using log4net;
34using Mono.Addins; 39using Mono.Addins;
@@ -38,9 +43,7 @@ using OpenSim.Region.CoreModules.Framework.InterfaceCommander;
38using OpenSim.Region.Framework.Interfaces; 43using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes; 44using OpenSim.Region.Framework.Scenes;
40 45
41using System.Xml; 46using Timer= System.Timers.Timer;
42using System.Xml.Serialization;
43using System.IO;
44 47
45namespace OpenSim.Region.OptionalModules.World.TreePopulator 48namespace OpenSim.Region.OptionalModules.World.TreePopulator
46{ 49{
@@ -48,7 +51,7 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
48 /// Version 2.02 - Still hacky 51 /// Version 2.02 - Still hacky
49 /// </summary> 52 /// </summary>
50 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "TreePopulatorModule")] 53 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "TreePopulatorModule")]
51 public class TreePopulatorModule : INonSharedRegionModule, ICommandableModule, IVegetationModule 54 public class TreePopulatorModule : INonSharedRegionModule, ICommandableModule
52 { 55 {
53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 56 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54 private readonly Commander m_commander = new Commander("tree"); 57 private readonly Commander m_commander = new Commander("tree");
@@ -82,20 +85,20 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
82 { 85 {
83 Copse cp = (Copse)DeserializeObject(fileName); 86 Copse cp = (Copse)DeserializeObject(fileName);
84 87
85 this.m_name = cp.m_name; 88 m_name = cp.m_name;
86 this.m_frozen = cp.m_frozen; 89 m_frozen = cp.m_frozen;
87 this.m_tree_quantity = cp.m_tree_quantity; 90 m_tree_quantity = cp.m_tree_quantity;
88 this.m_treeline_high = cp.m_treeline_high; 91 m_treeline_high = cp.m_treeline_high;
89 this.m_treeline_low = cp.m_treeline_low; 92 m_treeline_low = cp.m_treeline_low;
90 this.m_range = cp.m_range; 93 m_range = cp.m_range;
91 this.m_tree_type = cp.m_tree_type; 94 m_tree_type = cp.m_tree_type;
92 this.m_seed_point = cp.m_seed_point; 95 m_seed_point = cp.m_seed_point;
93 this.m_initial_scale = cp.m_initial_scale; 96 m_initial_scale = cp.m_initial_scale;
94 this.m_maximum_scale = cp.m_maximum_scale; 97 m_maximum_scale = cp.m_maximum_scale;
95 this.m_initial_scale = cp.m_initial_scale; 98 m_initial_scale = cp.m_initial_scale;
96 this.m_rate = cp.m_rate; 99 m_rate = cp.m_rate;
97 this.m_planted = planted; 100 m_planted = planted;
98 this.m_trees = new List<UUID>(); 101 m_trees = new List<UUID>();
99 } 102 }
100 103
101 public Copse(string copsedef) 104 public Copse(string copsedef)
@@ -103,61 +106,63 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
103 char[] delimiterChars = {':', ';'}; 106 char[] delimiterChars = {':', ';'};
104 string[] field = copsedef.Split(delimiterChars); 107 string[] field = copsedef.Split(delimiterChars);
105 108
106 this.m_name = field[1].Trim(); 109 m_name = field[1].Trim();
107 this.m_frozen = (copsedef[0] == 'F'); 110 m_frozen = (copsedef[0] == 'F');
108 this.m_tree_quantity = int.Parse(field[2]); 111 m_tree_quantity = int.Parse(field[2]);
109 this.m_treeline_high = float.Parse(field[3], Culture.NumberFormatInfo); 112 m_treeline_high = float.Parse(field[3], Culture.NumberFormatInfo);
110 this.m_treeline_low = float.Parse(field[4], Culture.NumberFormatInfo); 113 m_treeline_low = float.Parse(field[4], Culture.NumberFormatInfo);
111 this.m_range = double.Parse(field[5], Culture.NumberFormatInfo); 114 m_range = double.Parse(field[5], Culture.NumberFormatInfo);
112 this.m_tree_type = (Tree) Enum.Parse(typeof(Tree),field[6]); 115 m_tree_type = (Tree) Enum.Parse(typeof(Tree),field[6]);
113 this.m_seed_point = Vector3.Parse(field[7]); 116 m_seed_point = Vector3.Parse(field[7]);
114 this.m_initial_scale = Vector3.Parse(field[8]); 117 m_initial_scale = Vector3.Parse(field[8]);
115 this.m_maximum_scale = Vector3.Parse(field[9]); 118 m_maximum_scale = Vector3.Parse(field[9]);
116 this.m_rate = Vector3.Parse(field[10]); 119 m_rate = Vector3.Parse(field[10]);
117 this.m_planted = true; 120 m_planted = true;
118 this.m_trees = new List<UUID>(); 121 m_trees = new List<UUID>();
119 } 122 }
120 123
121 public Copse(string name, int quantity, float high, float low, double range, Vector3 point, Tree type, Vector3 scale, Vector3 max_scale, Vector3 rate, List<UUID> trees) 124 public Copse(string name, int quantity, float high, float low, double range, Vector3 point, Tree type, Vector3 scale, Vector3 max_scale, Vector3 rate, List<UUID> trees)
122 { 125 {
123 this.m_name = name; 126 m_name = name;
124 this.m_frozen = false; 127 m_frozen = false;
125 this.m_tree_quantity = quantity; 128 m_tree_quantity = quantity;
126 this.m_treeline_high = high; 129 m_treeline_high = high;
127 this.m_treeline_low = low; 130 m_treeline_low = low;
128 this.m_range = range; 131 m_range = range;
129 this.m_tree_type = type; 132 m_tree_type = type;
130 this.m_seed_point = point; 133 m_seed_point = point;
131 this.m_initial_scale = scale; 134 m_initial_scale = scale;
132 this.m_maximum_scale = max_scale; 135 m_maximum_scale = max_scale;
133 this.m_rate = rate; 136 m_rate = rate;
134 this.m_planted = false; 137 m_planted = false;
135 this.m_trees = trees; 138 m_trees = trees;
136 } 139 }
137 140
138 public override string ToString() 141 public override string ToString()
139 { 142 {
140 string frozen = (this.m_frozen ? "F" : "A"); 143 string frozen = (m_frozen ? "F" : "A");
141 144
142 return string.Format("{0}TPM: {1}; {2}; {3:0.0}; {4:0.0}; {5:0.0}; {6}; {7:0.0}; {8:0.0}; {9:0.0}; {10:0.00};", 145 return string.Format("{0}TPM: {1}; {2}; {3:0.0}; {4:0.0}; {5:0.0}; {6}; {7:0.0}; {8:0.0}; {9:0.0}; {10:0.00};",
143 frozen, 146 frozen,
144 this.m_name, 147 m_name,
145 this.m_tree_quantity, 148 m_tree_quantity,
146 this.m_treeline_high, 149 m_treeline_high,
147 this.m_treeline_low, 150 m_treeline_low,
148 this.m_range, 151 m_range,
149 this.m_tree_type, 152 m_tree_type,
150 this.m_seed_point.ToString(), 153 m_seed_point.ToString(),
151 this.m_initial_scale.ToString(), 154 m_initial_scale.ToString(),
152 this.m_maximum_scale.ToString(), 155 m_maximum_scale.ToString(),
153 this.m_rate.ToString()); 156 m_rate.ToString());
154 } 157 }
155 } 158 }
156 159
157 private List<Copse> m_copse; 160 private List<Copse> m_copses = new List<Copse>();
158 161 private object mylock;
159 private double m_update_ms = 1000.0; // msec between updates 162 private double m_update_ms = 1000.0; // msec between updates
160 private bool m_active_trees = false; 163 private bool m_active_trees = false;
164 private bool m_enabled = true; // original default
165 private bool m_allowGrow = true; // original default
161 166
162 Timer CalculateTrees; 167 Timer CalculateTrees;
163 168
@@ -174,51 +179,51 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
174 179
175 public void Initialise(IConfigSource config) 180 public void Initialise(IConfigSource config)
176 { 181 {
177 182 IConfig moduleConfig = config.Configs["Trees"];
178 // ini file settings 183 if (moduleConfig != null)
179 try
180 {
181 m_active_trees = config.Configs["Trees"].GetBoolean("active_trees", m_active_trees);
182 }
183 catch (Exception)
184 { 184 {
185 m_log.Debug("[TREES]: ini failure for active_trees - using default"); 185 m_enabled = moduleConfig.GetBoolean("enabled", m_enabled);
186 m_active_trees = moduleConfig.GetBoolean("active_trees", m_active_trees);
187 m_allowGrow = moduleConfig.GetBoolean("allowGrow", m_allowGrow);
188 m_update_ms = moduleConfig.GetDouble("update_rate", m_update_ms);
186 } 189 }
187 190
188 try 191 if(!m_enabled)
189 { 192 return;
190 m_update_ms = config.Configs["Trees"].GetDouble("update_rate", m_update_ms); 193
191 } 194 m_copses = new List<Copse>();
192 catch (Exception) 195 mylock = new object();
193 {
194 m_log.Debug("[TREES]: ini failure for update_rate - using default");
195 }
196 196
197 InstallCommands(); 197 InstallCommands();
198 198
199 m_log.Debug("[TREES]: Initialised tree module"); 199 m_log.Debug("[TREES]: Initialised tree populator module");
200 } 200 }
201 201
202 public void AddRegion(Scene scene) 202 public void AddRegion(Scene scene)
203 { 203 {
204 if(!m_enabled)
205 return;
204 m_scene = scene; 206 m_scene = scene;
205 m_scene.RegisterModuleCommander(m_commander); 207 m_scene.RegisterModuleCommander(m_commander);
206 m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; 208 m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole;
207 209 m_scene.EventManager.OnPrimsLoaded += EventManager_OnPrimsLoaded;
208 } 210 }
209 211
210 public void RemoveRegion(Scene scene) 212 public void RemoveRegion(Scene scene)
211 { 213 {
212 } 214 if(!m_enabled)
215 return;
216 if(m_active_trees && CalculateTrees != null)
217 {
218 CalculateTrees.Dispose();
219 CalculateTrees = null;
220 }
221 m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole;
222 m_scene.EventManager.OnPrimsLoaded -= EventManager_OnPrimsLoaded;
223 }
213 224
214 public void RegionLoaded(Scene scene) 225 public void RegionLoaded(Scene scene)
215 { 226 {
216 ReloadCopse();
217 if (m_copse.Count > 0)
218 m_log.Info("[TREES]: Copse load complete");
219
220 if (m_active_trees)
221 activeizeTreeze(true);
222 } 227 }
223 228
224 public void Close() 229 public void Close()
@@ -240,6 +245,16 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
240 245
241 //-------------------------------------------------------------- 246 //--------------------------------------------------------------
242 247
248 private void EventManager_OnPrimsLoaded(Scene s)
249 {
250 ReloadCopse();
251 if (m_copses.Count > 0)
252 m_log.Info("[TREES]: Copses loaded" );
253
254 if (m_active_trees)
255 activeizeTreeze(true);
256 }
257
243 #region ICommandableModule Members 258 #region ICommandableModule Members
244 259
245 private void HandleTreeActive(Object[] args) 260 private void HandleTreeActive(Object[] args)
@@ -267,25 +282,57 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
267 string copsename = ((string)args[0]).Trim(); 282 string copsename = ((string)args[0]).Trim();
268 Boolean freezeState = (Boolean) args[1]; 283 Boolean freezeState = (Boolean) args[1];
269 284
270 foreach (Copse cp in m_copse) 285 lock(mylock)
271 { 286 {
272 if (cp.m_name == copsename && (!cp.m_frozen && freezeState || cp.m_frozen && !freezeState)) 287 foreach (Copse cp in m_copses)
273 { 288 {
274 cp.m_frozen = freezeState; 289 if (cp.m_name != copsename)
275 foreach (UUID tree in cp.m_trees) 290 continue;
291
292 if(!cp.m_frozen && freezeState || cp.m_frozen && !freezeState)
276 { 293 {
277 SceneObjectPart sop = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart; 294 cp.m_frozen = freezeState;
278 sop.Name = (freezeState ? sop.Name.Replace("ATPM", "FTPM") : sop.Name.Replace("FTPM", "ATPM")); 295 List<UUID> losttrees = new List<UUID>();
279 sop.ParentGroup.HasGroupChanged = true; 296 foreach (UUID tree in cp.m_trees)
280 } 297 {
298 SceneObjectGroup sog = m_scene.GetSceneObjectGroup(tree);
299 if(sog != null && !sog.IsDeleted)
300 {
301 SceneObjectPart sop = sog.RootPart;
302 string name = sop.Name;
303 if(freezeState)
304 {
305 if(name.StartsWith("FTPM"))
306 continue;
307 if(!name.StartsWith("ATPM"))
308 continue;
309 sop.Name = sop.Name.Replace("ATPM", "FTPM");
310 }
311 else
312 {
313 if(name.StartsWith("ATPM"))
314 continue;
315 if(!name.StartsWith("FTPM"))
316 continue;
317 sop.Name = sop.Name.Replace("FTPM", "ATPM");
318 }
319 sop.ParentGroup.HasGroupChanged = true;
320 sog.ScheduleGroupForFullUpdate();
321 }
322 else
323 losttrees.Add(tree);
324 }
325 foreach (UUID tree in losttrees)
326 cp.m_trees.Remove(tree);
281 327
282 m_log.InfoFormat("[TREES]: Activity for copse {0} is frozen {1}", copsename, freezeState); 328 m_log.InfoFormat("[TREES]: Activity for copse {0} is frozen {1}", copsename, freezeState);
283 return; 329 return;
284 } 330 }
285 else if (cp.m_name == copsename && (cp.m_frozen && freezeState || !cp.m_frozen && !freezeState)) 331 else
286 { 332 {
287 m_log.InfoFormat("[TREES]: Copse {0} is already in the requested freeze state", copsename); 333 m_log.InfoFormat("[TREES]: Copse {0} is already in the requested freeze state", copsename);
288 return; 334 return;
335 }
289 } 336 }
290 } 337 }
291 m_log.InfoFormat("[TREES]: Copse {0} was not found - command failed", copsename); 338 m_log.InfoFormat("[TREES]: Copse {0} was not found - command failed", copsename);
@@ -297,17 +344,21 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
297 344
298 m_log.InfoFormat("[TREES]: Loading copse definition...."); 345 m_log.InfoFormat("[TREES]: Loading copse definition....");
299 346
300 copse = new Copse(((string)args[0]), false); 347 lock(mylock)
301 foreach (Copse cp in m_copse)
302 { 348 {
303 if (cp.m_name == copse.m_name) 349 copse = new Copse(((string)args[0]), false);
304 { 350 {
305 m_log.InfoFormat("[TREES]: Copse: {0} is already defined - command failed", copse.m_name); 351 foreach (Copse cp in m_copses)
306 return; 352 {
353 if (cp.m_name == copse.m_name)
354 {
355 m_log.InfoFormat("[TREES]: Copse: {0} is already defined - command failed", copse.m_name);
356 return;
357 }
358 }
307 } 359 }
360 m_copses.Add(copse);
308 } 361 }
309
310 m_copse.Add(copse);
311 m_log.InfoFormat("[TREES]: Loaded copse: {0}", copse.ToString()); 362 m_log.InfoFormat("[TREES]: Loaded copse: {0}", copse.ToString());
312 } 363 }
313 364
@@ -318,20 +369,24 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
318 m_log.InfoFormat("[TREES]: New tree planting for copse {0}", copsename); 369 m_log.InfoFormat("[TREES]: New tree planting for copse {0}", copsename);
319 UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner; 370 UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner;
320 371
321 foreach (Copse copse in m_copse) 372 lock(mylock)
322 { 373 {
323 if (copse.m_name == copsename) 374 foreach (Copse copse in m_copses)
324 { 375 {
325 if (!copse.m_planted) 376 if (copse.m_name == copsename)
326 {
327 // The first tree for a copse is created here
328 CreateTree(uuid, copse, copse.m_seed_point);
329 copse.m_planted = true;
330 return;
331 }
332 else
333 { 377 {
334 m_log.InfoFormat("[TREES]: Copse {0} has already been planted", copsename); 378 if (!copse.m_planted)
379 {
380 // The first tree for a copse is created here
381 CreateTree(uuid, copse, copse.m_seed_point, true);
382 copse.m_planted = true;
383 return;
384 }
385 else
386 {
387 m_log.InfoFormat("[TREES]: Copse {0} has already been planted", copsename);
388 return;
389 }
335 } 390 }
336 } 391 }
337 } 392 }
@@ -376,45 +431,49 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
376 string copsename = ((string)args[0]).Trim(); 431 string copsename = ((string)args[0]).Trim();
377 Copse copseIdentity = null; 432 Copse copseIdentity = null;
378 433
379 foreach (Copse cp in m_copse) 434 lock(mylock)
380 { 435 {
381 if (cp.m_name == copsename) 436 foreach (Copse cp in m_copses)
382 { 437 {
383 copseIdentity = cp; 438 if (cp.m_name == copsename)
439 {
440 copseIdentity = cp;
441 }
384 } 442 }
385 }
386 443
387 if (copseIdentity != null) 444 if (copseIdentity != null)
388 {
389 foreach (UUID tree in copseIdentity.m_trees)
390 { 445 {
391 if (m_scene.Entities.ContainsKey(tree)) 446 foreach (UUID tree in copseIdentity.m_trees)
392 { 447 {
393 SceneObjectPart selectedTree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart; 448 if (m_scene.Entities.ContainsKey(tree))
394 // Delete tree and alert clients (not silent) 449 {
395 m_scene.DeleteSceneObject(selectedTree.ParentGroup, false); 450 SceneObjectPart selectedTree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
396 } 451 // Delete tree and alert clients (not silent)
397 else 452 m_scene.DeleteSceneObject(selectedTree.ParentGroup, false);
398 { 453 }
399 m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree); 454 else
455 {
456 m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree);
457 }
400 } 458 }
459 copseIdentity.m_trees = null;
460 m_copses.Remove(copseIdentity);
461 m_log.InfoFormat("[TREES]: Copse {0} has been removed", copsename);
462 }
463 else
464 {
465 m_log.InfoFormat("[TREES]: Copse {0} was not found - command failed", copsename);
401 } 466 }
402 copseIdentity.m_trees = new List<UUID>();
403 m_copse.Remove(copseIdentity);
404 m_log.InfoFormat("[TREES]: Copse {0} has been removed", copsename);
405 }
406 else
407 {
408 m_log.InfoFormat("[TREES]: Copse {0} was not found - command failed", copsename);
409 } 467 }
410 } 468 }
411 469
412 private void HandleTreeStatistics(Object[] args) 470 private void HandleTreeStatistics(Object[] args)
413 { 471 {
414 m_log.InfoFormat("[TREES]: Activity State: {0}; Update Rate: {1}", m_active_trees, m_update_ms); 472 m_log.InfoFormat("[TREES]: region {0}:", m_scene.Name);
415 foreach (Copse cp in m_copse) 473 m_log.InfoFormat("[TREES]: Activity State: {0}; Update Rate: {1}", m_active_trees, m_update_ms);
474 foreach (Copse cp in m_copses)
416 { 475 {
417 m_log.InfoFormat("[TREES]: Copse {0}; {1} trees; frozen {2}", cp.m_name, cp.m_trees.Count, cp.m_frozen); 476 m_log.InfoFormat("[TREES]: Copse {0}; {1} trees; frozen {2}", cp.m_name, cp.m_trees.Count, cp.m_frozen);
418 } 477 }
419 } 478 }
420 479
@@ -442,7 +501,7 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
442 treeRateCommand.AddArgument("updateRate", "The required update rate (minimum 1000.0)", "Double"); 501 treeRateCommand.AddArgument("updateRate", "The required update rate (minimum 1000.0)", "Double");
443 502
444 Command treeReloadCommand = 503 Command treeReloadCommand =
445 new Command("reload", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeReload, "Reload copse definitions from the in-scene trees"); 504 new Command("reload", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeReload, "Reload copses from the in-scene trees");
446 505
447 Command treeRemoveCommand = 506 Command treeRemoveCommand =
448 new Command("remove", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeRemove, "Remove a copse definition and all its in-scene trees"); 507 new Command("remove", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeRemove, "Remove a copse definition and all its in-scene trees");
@@ -499,34 +558,17 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
499 treeShape.Scale = scale; 558 treeShape.Scale = scale;
500 treeShape.State = (byte)treeType; 559 treeShape.State = (byte)treeType;
501 560
502 return m_scene.AddNewPrim(uuid, groupID, position, rotation, treeShape); 561 SceneObjectGroup sog = new SceneObjectGroup(uuid, position, rotation, treeShape);
503 } 562 SceneObjectPart rootPart = sog.RootPart;
504
505 #endregion
506
507 #region IEntityCreator Members
508
509 protected static readonly PCode[] creationCapabilities = new PCode[] { PCode.NewTree, PCode.Tree };
510 public PCode[] CreationCapabilities { get { return creationCapabilities; } }
511
512 public SceneObjectGroup CreateEntity(
513 UUID ownerID, UUID groupID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape)
514 {
515 if (Array.IndexOf(creationCapabilities, (PCode)shape.PCode) < 0)
516 {
517 m_log.DebugFormat("[VEGETATION]: PCode {0} not handled by {1}", shape.PCode, Name);
518 return null;
519 }
520
521 SceneObjectGroup sceneObject = new SceneObjectGroup(ownerID, pos, rot, shape);
522 SceneObjectPart rootPart = sceneObject.GetPart(sceneObject.UUID);
523 563
524 rootPart.AddFlag(PrimFlags.Phantom); 564 rootPart.AddFlag(PrimFlags.Phantom);
525 565
526 sceneObject.SetGroup(groupID, null); 566 sog.SetGroup(groupID, null);
527 m_scene.AddNewSceneObject(sceneObject, true); 567 m_scene.AddNewSceneObject(sog, true, false);
528 sceneObject.InvalidateEffectivePerms(); 568 sog.IsSelected = false;
529 return sceneObject; 569 rootPart.IsSelected = false;
570 sog.InvalidateEffectivePerms();
571 return sog;
530 } 572 }
531 573
532 #endregion 574 #endregion
@@ -569,26 +611,27 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
569 611
570 private void ReloadCopse() 612 private void ReloadCopse()
571 { 613 {
572 m_copse = new List<Copse>(); 614 m_copses = new List<Copse>();
573 615
574 EntityBase[] objs = m_scene.GetEntities(); 616 List<SceneObjectGroup> grps = m_scene.GetSceneObjectGroups();
575 foreach (EntityBase obj in objs) 617 foreach (SceneObjectGroup grp in grps)
576 { 618 {
577 if (obj is SceneObjectGroup) 619 if(grp.RootPart.Shape.PCode != (byte)PCode.NewTree && grp.RootPart.Shape.PCode != (byte)PCode.Tree)
578 { 620 continue;
579 SceneObjectGroup grp = (SceneObjectGroup)obj;
580 621
581 if (grp.Name.Length > 5 && (grp.Name.Substring(0, 5) == "ATPM:" || grp.Name.Substring(0, 5) == "FTPM:")) 622 if (grp.Name.Length > 5 && (grp.Name.Substring(0, 5) == "ATPM:" || grp.Name.Substring(0, 5) == "FTPM:"))
623 {
624 // Create a new copse definition or add uuid to an existing definition
625 try
582 { 626 {
583 // Create a new copse definition or add uuid to an existing definition 627 Boolean copsefound = false;
584 try 628 Copse grpcopse = new Copse(grp.Name);
585 {
586 Boolean copsefound = false;
587 Copse copse = new Copse(grp.Name);
588 629
589 foreach (Copse cp in m_copse) 630 lock(mylock)
631 {
632 foreach (Copse cp in m_copses)
590 { 633 {
591 if (cp.m_name == copse.m_name) 634 if (cp.m_name == grpcopse.m_name)
592 { 635 {
593 copsefound = true; 636 copsefound = true;
594 cp.m_trees.Add(grp.UUID); 637 cp.m_trees.Add(grp.UUID);
@@ -598,15 +641,15 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
598 641
599 if (!copsefound) 642 if (!copsefound)
600 { 643 {
601 m_log.InfoFormat("[TREES]: Found copse {0}", grp.Name); 644 m_log.InfoFormat("[TREES]: adding copse {0}", grpcopse.m_name);
602 m_copse.Add(copse); 645 grpcopse.m_trees.Add(grp.UUID);
603 copse.m_trees.Add(grp.UUID); 646 m_copses.Add(grpcopse);
604 } 647 }
605 } 648 }
606 catch 649 }
607 { 650 catch
608 m_log.InfoFormat("[TREES]: Ill formed copse definition {0} - ignoring", grp.Name); 651 {
609 } 652 m_log.InfoFormat("[TREES]: Ill formed copse definition {0} - ignoring", grp.Name);
610 } 653 }
611 } 654 }
612 } 655 }
@@ -617,8 +660,10 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
617 { 660 {
618 if (activeYN) 661 if (activeYN)
619 { 662 {
620 CalculateTrees = new Timer(m_update_ms); 663 if(CalculateTrees == null)
664 CalculateTrees = new Timer(m_update_ms);
621 CalculateTrees.Elapsed += CalculateTrees_Elapsed; 665 CalculateTrees.Elapsed += CalculateTrees_Elapsed;
666 CalculateTrees.AutoReset = false;
622 CalculateTrees.Start(); 667 CalculateTrees.Start();
623 } 668 }
624 else 669 else
@@ -629,154 +674,251 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator
629 674
630 private void growTrees() 675 private void growTrees()
631 { 676 {
632 foreach (Copse copse in m_copse) 677 if(!m_allowGrow)
678 return;
679
680 foreach (Copse copse in m_copses)
633 { 681 {
634 if (!copse.m_frozen) 682 if (copse.m_frozen)
683 continue;
684
685 if(copse.m_trees.Count == 0)
686 continue;
687
688 float maxscale = copse.m_maximum_scale.Z;
689 float ratescale = 1.0f;
690 List<UUID> losttrees = new List<UUID>();
691 foreach (UUID tree in copse.m_trees)
635 { 692 {
636 foreach (UUID tree in copse.m_trees) 693 SceneObjectGroup sog = m_scene.GetSceneObjectGroup(tree);
637 {
638 if (m_scene.Entities.ContainsKey(tree))
639 {
640 SceneObjectPart s_tree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
641 694
642 if (s_tree.Scale.X < copse.m_maximum_scale.X && s_tree.Scale.Y < copse.m_maximum_scale.Y && s_tree.Scale.Z < copse.m_maximum_scale.Z) 695 if (sog != null && !sog.IsDeleted)
643 { 696 {
644 s_tree.Scale += copse.m_rate; 697 SceneObjectPart s_tree = sog.RootPart;
645 s_tree.ParentGroup.HasGroupChanged = true; 698 if (s_tree.Scale.Z < maxscale)
646 s_tree.ScheduleFullUpdate();
647 }
648 }
649 else
650 { 699 {
651 m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree); 700 ratescale = (float)Util.RandomClass.NextDouble();
701 if(ratescale < 0.2f)
702 ratescale = 0.2f;
703 s_tree.Scale += copse.m_rate * ratescale;
704 sog.HasGroupChanged = true;
705 s_tree.ScheduleFullUpdate();
652 } 706 }
653 } 707 }
708 else
709 losttrees.Add(tree);
654 } 710 }
711
712 foreach (UUID tree in losttrees)
713 copse.m_trees.Remove(tree);
655 } 714 }
656 } 715 }
657 716
658 private void seedTrees() 717 private void seedTrees()
659 { 718 {
660 foreach (Copse copse in m_copse) 719 foreach (Copse copse in m_copses)
661 { 720 {
662 if (!copse.m_frozen) 721 if (copse.m_frozen)
722 continue;
723
724 if(copse.m_trees.Count == 0)
725 return;
726
727 bool low = copse.m_trees.Count < (int)(copse.m_tree_quantity * 0.8f);
728
729 if (!low && Util.RandomClass.NextDouble() < 0.75)
730 return;
731
732 int maxbirths = (int)(copse.m_tree_quantity) - copse.m_trees.Count;
733 if(maxbirths <= 1)
734 return;
735
736 if(maxbirths > 20)
737 maxbirths = 20;
738
739 float minscale = 0;
740 if(!low && m_allowGrow)
741 minscale = copse.m_maximum_scale.Z * 0.75f;;
742
743 int i = 0;
744 UUID[] current = copse.m_trees.ToArray();
745 while(--maxbirths > 0)
663 { 746 {
664 foreach (UUID tree in copse.m_trees) 747 if(current.Length > 1)
748 i = Util.RandomClass.Next(current.Length -1);
749
750 UUID tree = current[i];
751 SceneObjectGroup sog = m_scene.GetSceneObjectGroup(tree);
752
753 if (sog != null && !sog.IsDeleted)
665 { 754 {
666 if (m_scene.Entities.ContainsKey(tree)) 755 SceneObjectPart s_tree = sog.RootPart;
667 {
668 SceneObjectPart s_tree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
669 756
670 if (copse.m_trees.Count < copse.m_tree_quantity) 757 // Tree has grown enough to seed if it has grown by at least 25% of seeded to full grown height
671 { 758 if (s_tree.Scale.Z > minscale)
672 // Tree has grown enough to seed if it has grown by at least 25% of seeded to full grown height 759 SpawnChild(copse, s_tree, true);
673 if (s_tree.Scale.Z > copse.m_initial_scale.Z + (copse.m_maximum_scale.Z - copse.m_initial_scale.Z) / 4.0)
674 {
675 if (Util.RandomClass.NextDouble() > 0.75)
676 {
677 SpawnChild(copse, s_tree);
678 }
679 }
680 }
681 }
682 else
683 {
684 m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree);
685 }
686 } 760 }
687 } 761 else if(copse.m_trees.Contains(tree))
762 copse.m_trees.Remove(tree);
763 }
688 } 764 }
689 } 765 }
690 766
691 private void killTrees() 767 private void killTrees()
692 { 768 {
693 foreach (Copse copse in m_copse) 769 foreach (Copse copse in m_copses)
694 { 770 {
695 if (!copse.m_frozen && copse.m_trees.Count >= copse.m_tree_quantity) 771 if (copse.m_frozen)
696 { 772 continue;
697 foreach (UUID tree in copse.m_trees)
698 {
699 double killLikelyhood = 0.0;
700
701 if (m_scene.Entities.ContainsKey(tree))
702 {
703 SceneObjectPart selectedTree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
704 double selectedTreeScale = Math.Sqrt(Math.Pow(selectedTree.Scale.X, 2) +
705 Math.Pow(selectedTree.Scale.Y, 2) +
706 Math.Pow(selectedTree.Scale.Z, 2));
707 773
708 foreach (UUID picktree in copse.m_trees) 774 if (Util.RandomClass.NextDouble() < 0.25)
709 { 775 return;
710 if (picktree != tree)
711 {
712 SceneObjectPart pickedTree = ((SceneObjectGroup)m_scene.Entities[picktree]).RootPart;
713
714 double pickedTreeScale = Math.Sqrt(Math.Pow(pickedTree.Scale.X, 2) +
715 Math.Pow(pickedTree.Scale.Y, 2) +
716 Math.Pow(pickedTree.Scale.Z, 2));
717 776
718 double pickedTreeDistance = Vector3.Distance(pickedTree.AbsolutePosition, selectedTree.AbsolutePosition); 777 int maxbdeaths = copse.m_trees.Count - (int)(copse.m_tree_quantity * .98f) ;
778 if(maxbdeaths < 1)
779 return;
719 780
720 killLikelyhood += (selectedTreeScale / (pickedTreeScale * pickedTreeDistance)) * 0.1; 781 float odds;
721 } 782 float scale = 1.0f / copse.m_maximum_scale.Z;
722 }
723 783
724 if (Util.RandomClass.NextDouble() < killLikelyhood) 784 int ntries = maxbdeaths * 4;
725 { 785 while(ntries-- > 0 )
726 // Delete tree and alert clients (not silent) 786 {
727 m_scene.DeleteSceneObject(selectedTree.ParentGroup, false); 787 int next = 0;
728 copse.m_trees.Remove(selectedTree.ParentGroup.UUID); 788 if (copse.m_trees.Count > 1)
729 break; 789 next = Util.RandomClass.Next(copse.m_trees.Count - 1);
730 } 790 UUID tree = copse.m_trees[next];
791 SceneObjectGroup sog = m_scene.GetSceneObjectGroup(tree);
792 if (sog != null && !sog.IsDeleted)
793 {
794 if(m_allowGrow)
795 {
796 odds = sog.RootPart.Scale.Z * scale;
797 odds = odds * odds * odds;
798 odds *= (float)Util.RandomClass.NextDouble();
731 } 799 }
732 else 800 else
733 { 801 {
734 m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree); 802 odds = (float)Util.RandomClass.NextDouble();
803 odds = odds * odds * odds;
804 }
805
806 if(odds > 0.9f)
807 {
808 m_scene.DeleteSceneObject(sog, false);
809 if(maxbdeaths <= 0)
810 break;
735 } 811 }
812 }
813 else
814 {
815 copse.m_trees.Remove(tree);
816 if(copse.m_trees.Count - (int)(copse.m_tree_quantity * .98f) <= 0 )
817 break;
736 } 818 }
737 } 819 }
738 } 820 }
739 } 821 }
740 822
741 private void SpawnChild(Copse copse, SceneObjectPart s_tree) 823 private void SpawnChild(Copse copse, SceneObjectPart s_tree, bool low)
742 { 824 {
743 Vector3 position = new Vector3(); 825 Vector3 position = new Vector3();
744 826
745 double randX = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3); 827 float randX = copse.m_maximum_scale.X * 1.25f;
746 double randY = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3); 828 float randY = copse.m_maximum_scale.Y * 1.25f;
747 829
830 float r = (float)Util.RandomClass.NextDouble();
831 randX *= 2.0f * r - 1.0f;
748 position.X = s_tree.AbsolutePosition.X + (float)randX; 832 position.X = s_tree.AbsolutePosition.X + (float)randX;
833
834 r = (float)Util.RandomClass.NextDouble();
835 randY *= 2.0f * r - 1.0f;
749 position.Y = s_tree.AbsolutePosition.Y + (float)randY; 836 position.Y = s_tree.AbsolutePosition.Y + (float)randY;
750 837
751 if (position.X <= (m_scene.RegionInfo.RegionSizeX - 1) && position.X >= 0 && 838 if (position.X > (m_scene.RegionInfo.RegionSizeX - 1) || position.X <= 0 ||
752 position.Y <= (m_scene.RegionInfo.RegionSizeY - 1) && position.Y >= 0 && 839 position.Y > (m_scene.RegionInfo.RegionSizeY - 1) || position.Y <= 0)
753 Util.GetDistanceTo(position, copse.m_seed_point) <= copse.m_range) 840 return;
754 {
755 UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner;
756 841
757 CreateTree(uuid, copse, position); 842 randX = position.X - copse.m_seed_point.X;
758 } 843 randX *= randX;
844 randY = position.Y - copse.m_seed_point.Y;
845 randY *= randY;
846 randX += randY;
847
848 if(randX > copse.m_range * copse.m_range)
849 return;
850
851 UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner;
852 CreateTree(uuid, copse, position, low);
759 } 853 }
760 854
761 private void CreateTree(UUID uuid, Copse copse, Vector3 position) 855 private void CreateTree(UUID uuid, Copse copse, Vector3 position, bool randomScale)
762 { 856 {
763
764 position.Z = (float)m_scene.Heightmap[(int)position.X, (int)position.Y]; 857 position.Z = (float)m_scene.Heightmap[(int)position.X, (int)position.Y];
765 if (position.Z >= copse.m_treeline_low && position.Z <= copse.m_treeline_high) 858 if (position.Z < copse.m_treeline_low || position.Z > copse.m_treeline_high)
766 { 859 return;
767 SceneObjectGroup tree = AddTree(uuid, UUID.Zero, copse.m_initial_scale, Quaternion.Identity, position, copse.m_tree_type, false);
768 860
769 tree.Name = copse.ToString(); 861 Vector3 scale = copse.m_initial_scale;
770 copse.m_trees.Add(tree.UUID); 862 if(randomScale)
771 tree.SendGroupFullUpdate(); 863 {
864 try
865 {
866 float t;
867 float r = (float)Util.RandomClass.NextDouble();
868 r *= (float)Util.RandomClass.NextDouble();
869 r *= (float)Util.RandomClass.NextDouble();
870
871 t = copse.m_maximum_scale.X / copse.m_initial_scale.X;
872 if(t < 1.0)
873 t = 1 / t;
874 t = t * r + 1.0f;
875 scale.X *= t;
876
877 t = copse.m_maximum_scale.Y / copse.m_initial_scale.Y;
878 if(t < 1.0)
879 t = 1 / t;
880 t = t * r + 1.0f;
881 scale.Y *= t;
882
883 t = copse.m_maximum_scale.Z / copse.m_initial_scale.Z;
884 if(t < 1.0)
885 t = 1 / t;
886 t = t * r + 1.0f;
887 scale.Z *= t;
888 }
889 catch
890 {
891 scale = copse.m_initial_scale;
892 }
772 } 893 }
894
895 SceneObjectGroup tree = AddTree(uuid, UUID.Zero, scale, Quaternion.Identity, position, copse.m_tree_type, false);
896 tree.Name = copse.ToString();
897 copse.m_trees.Add(tree.UUID);
898 tree.RootPart.ScheduleFullUpdate();
773 } 899 }
774 900
775 private void CalculateTrees_Elapsed(object sender, ElapsedEventArgs e) 901 private void CalculateTrees_Elapsed(object sender, ElapsedEventArgs e)
776 { 902 {
777 growTrees(); 903 if(!m_scene.IsRunning)
778 seedTrees(); 904 return;
779 killTrees(); 905
906 if(Monitor.TryEnter(mylock))
907 {
908 try
909 {
910 if(m_scene.LoginsEnabled )
911 {
912 growTrees();
913 seedTrees();
914 killTrees();
915 }
916 }
917 catch { }
918 if(CalculateTrees != null)
919 CalculateTrees.Start();
920 Monitor.Exit(mylock);
921 }
780 } 922 }
781 } 923 }
782} 924}