diff options
author | Charles Krinke | 2009-06-14 16:32:50 +0000 |
---|---|---|
committer | Charles Krinke | 2009-06-14 16:32:50 +0000 |
commit | 8d24168befd3bcecbdd19cdea091233eb113dfc7 (patch) | |
tree | 9027511fbca390c0b89947abff9b87f65e05ce5d | |
parent | Bug fix in remote neighbour connector. (diff) | |
download | opensim-SC-8d24168befd3bcecbdd19cdea091233eb113dfc7.zip opensim-SC-8d24168befd3bcecbdd19cdea091233eb113dfc7.tar.gz opensim-SC-8d24168befd3bcecbdd19cdea091233eb113dfc7.tar.bz2 opensim-SC-8d24168befd3bcecbdd19cdea091233eb113dfc7.tar.xz |
Thank you kindly, M1sha, for a patch that improves the treePopulator module:
(a) Implements the ICommandableModule interface to clean up the user interface
(b) Uses a specification for a 'copse' (collected group of trees) which permits via an xml file: Tree type; Tree Line (high and low), Seed point; Radius of Copse; Number of required trees; Initial size of seeded tree; maximum size of seeded tree; growth rate; freeze growth state
(c) Multiple Copse may be defined for a region
(d) Growth on individual copse may be frozen or restarted, or all growth disabled/enabled
(e) Copse definitions are persistant, they are reloaded from the trees present on a region restart
(f) All trees in a copse may be removed and the copse definition deleted in one command
-rw-r--r-- | OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs | 749 |
1 files changed, 612 insertions, 137 deletions
diff --git a/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs b/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs index c7d5027..3e9c326 100644 --- a/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs +++ b/OpenSim/Region/OptionalModules/World/TreePopulator/TreePopulatorModule.cs | |||
@@ -33,57 +33,182 @@ using OpenMetaverse; | |||
33 | using log4net; | 33 | using log4net; |
34 | using Nini.Config; | 34 | using Nini.Config; |
35 | using OpenSim.Framework; | 35 | using OpenSim.Framework; |
36 | using OpenSim.Region.CoreModules.Framework.InterfaceCommander; | ||
36 | using OpenSim.Region.Framework.Interfaces; | 37 | using OpenSim.Region.Framework.Interfaces; |
37 | using OpenSim.Region.Framework.Scenes; | 38 | using OpenSim.Region.Framework.Scenes; |
38 | 39 | ||
40 | using System.Xml; | ||
41 | using System.Xml.Serialization; | ||
42 | using System.IO; | ||
43 | |||
39 | namespace OpenSim.Region.OptionalModules.World.TreePopulator | 44 | namespace OpenSim.Region.OptionalModules.World.TreePopulator |
40 | { | 45 | { |
41 | /// <summary> | 46 | /// <summary> |
42 | /// Version 2.01 - Very hacky compared to the original. Will fix original and release as 0.3 later. | 47 | /// Version 2.02 - Still hacky |
43 | /// </summary> | 48 | /// </summary> |
44 | public class TreePopulatorModule : IRegionModule | 49 | public class TreePopulatorModule : IRegionModule, ICommandableModule, IVegetationModule |
45 | { | 50 | { |
46 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 51 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
52 | private readonly Commander m_commander = new Commander("tree"); | ||
47 | private Scene m_scene; | 53 | private Scene m_scene; |
48 | 54 | ||
49 | public double m_tree_density = 50.0; // Aim for this many per region | 55 | [XmlRootAttribute(ElementName = "Copse", IsNullable = false)] |
50 | public double m_tree_updates = 1000.0; // MS between updates | 56 | public class Copse |
57 | { | ||
58 | public string m_name; | ||
59 | public Boolean m_frozen; | ||
60 | public Tree m_tree_type; | ||
61 | public int m_tree_quantity; | ||
62 | public float m_treeline_low; | ||
63 | public float m_treeline_high; | ||
64 | public Vector3 m_seed_point; | ||
65 | public double m_range; | ||
66 | public Vector3 m_initial_scale; | ||
67 | public Vector3 m_maximum_scale; | ||
68 | public Vector3 m_rate; | ||
69 | |||
70 | [XmlIgnore] | ||
71 | public Boolean m_planted; | ||
72 | [XmlIgnore] | ||
73 | public List<UUID> m_trees; | ||
74 | |||
75 | public Copse() | ||
76 | { | ||
77 | } | ||
78 | |||
79 | public Copse(string fileName, Boolean planted) | ||
80 | { | ||
81 | Copse cp = (Copse)DeserializeObject(fileName); | ||
82 | |||
83 | this.m_name = cp.m_name; | ||
84 | this.m_frozen = cp.m_frozen; | ||
85 | this.m_tree_quantity = cp.m_tree_quantity; | ||
86 | this.m_treeline_high = cp.m_treeline_high; | ||
87 | this.m_treeline_low = cp.m_treeline_low; | ||
88 | this.m_range = cp.m_range; | ||
89 | this.m_tree_type = cp.m_tree_type; | ||
90 | this.m_seed_point = cp.m_seed_point; | ||
91 | this.m_initial_scale = cp.m_initial_scale; | ||
92 | this.m_maximum_scale = cp.m_maximum_scale; | ||
93 | this.m_initial_scale = cp.m_initial_scale; | ||
94 | this.m_rate = cp.m_rate; | ||
95 | this.m_planted = planted; | ||
96 | this.m_trees = new List<UUID>(); | ||
97 | } | ||
98 | |||
99 | public Copse(string copsedef) | ||
100 | { | ||
101 | char[] delimiterChars = {':', ';'}; | ||
102 | string[] field = copsedef.Split(delimiterChars); | ||
103 | |||
104 | this.m_name = field[1].Trim(); | ||
105 | this.m_frozen = (copsedef[0] == 'F'); | ||
106 | this.m_tree_quantity = int.Parse(field[2]); | ||
107 | this.m_treeline_high = float.Parse(field[3]); | ||
108 | this.m_treeline_low = float.Parse(field[4]); | ||
109 | this.m_range = double.Parse(field[5]); | ||
110 | this.m_tree_type = (Tree) Enum.Parse(typeof(Tree),field[6]); | ||
111 | this.m_seed_point = Vector3.Parse(field[7]); | ||
112 | this.m_initial_scale = Vector3.Parse(field[8]); | ||
113 | this.m_maximum_scale = Vector3.Parse(field[9]); | ||
114 | this.m_rate = Vector3.Parse(field[10]); | ||
115 | this.m_planted = true; | ||
116 | this.m_trees = new List<UUID>(); | ||
117 | } | ||
118 | |||
119 | 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) | ||
120 | { | ||
121 | this.m_name = name; | ||
122 | this.m_frozen = false; | ||
123 | this.m_tree_quantity = quantity; | ||
124 | this.m_treeline_high = high; | ||
125 | this.m_treeline_low = low; | ||
126 | this.m_range = range; | ||
127 | this.m_tree_type = type; | ||
128 | this.m_seed_point = point; | ||
129 | this.m_initial_scale = scale; | ||
130 | this.m_maximum_scale = max_scale; | ||
131 | this.m_rate = rate; | ||
132 | this.m_planted = false; | ||
133 | this.m_trees = trees; | ||
134 | } | ||
135 | |||
136 | public override string ToString() | ||
137 | { | ||
138 | string frozen = (this.m_frozen ? "F" : "A"); | ||
139 | |||
140 | 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};", | ||
141 | frozen, | ||
142 | this.m_name, | ||
143 | this.m_tree_quantity, | ||
144 | this.m_treeline_high, | ||
145 | this.m_treeline_low, | ||
146 | this.m_range, | ||
147 | this.m_tree_type, | ||
148 | this.m_seed_point.ToString(), | ||
149 | this.m_initial_scale.ToString(), | ||
150 | this.m_maximum_scale.ToString(), | ||
151 | this.m_rate.ToString()); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | private List<Copse> m_copse; | ||
156 | |||
157 | private double m_update_ms = 1000.0; // msec between updates | ||
51 | private bool m_active_trees = false; | 158 | private bool m_active_trees = false; |
52 | private List<UUID> m_trees; | 159 | |
53 | Timer CalculateTrees; | 160 | Timer CalculateTrees; |
54 | 161 | ||
162 | #region ICommandableModule Members | ||
163 | |||
164 | public ICommander CommandInterface | ||
165 | { | ||
166 | get { return m_commander; } | ||
167 | } | ||
168 | |||
169 | #endregion | ||
170 | |||
55 | #region IRegionModule Members | 171 | #region IRegionModule Members |
56 | 172 | ||
57 | public void Initialise(Scene scene, IConfigSource config) | 173 | public void Initialise(Scene scene, IConfigSource config) |
58 | { | 174 | { |
175 | |||
59 | m_scene = scene; | 176 | m_scene = scene; |
60 | m_scene.RegisterModuleInterface<IRegionModule>(this); | 177 | m_scene.RegisterModuleInterface<IRegionModule>(this); |
178 | m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; | ||
61 | 179 | ||
62 | m_scene.AddCommand( | 180 | // ini file settings |
63 | this, "tree plant", "tree plant", "Start populating trees", HandleTreeConsoleCommand); | ||
64 | |||
65 | m_scene.AddCommand( | ||
66 | this, "tree active", "tree active <boolean>", "Change activity state for trees module", HandleTreeConsoleCommand); | ||
67 | |||
68 | try | 181 | try |
69 | { | 182 | { |
70 | m_tree_density = config.Configs["Trees"].GetDouble("tree_density", m_tree_density); | ||
71 | m_active_trees = config.Configs["Trees"].GetBoolean("active_trees", m_active_trees); | 183 | m_active_trees = config.Configs["Trees"].GetBoolean("active_trees", m_active_trees); |
72 | } | 184 | } |
73 | catch (Exception) | 185 | catch (Exception) |
74 | { | 186 | { |
187 | m_log.Debug("[TREES]: ini failure for active_trees - using default"); | ||
75 | } | 188 | } |
76 | 189 | ||
77 | m_trees = new List<UUID>(); | 190 | try |
191 | { | ||
192 | m_update_ms = config.Configs["Trees"].GetDouble("update_rate", m_update_ms); | ||
193 | } | ||
194 | catch (Exception) | ||
195 | { | ||
196 | m_log.Debug("[TREES]: ini failure for update_rate - using default"); | ||
197 | } | ||
78 | 198 | ||
79 | if (m_active_trees) | 199 | InstallCommands(); |
80 | activeizeTreeze(true); | ||
81 | 200 | ||
82 | m_log.Debug("[TREES]: Initialised tree module"); | 201 | m_log.Debug("[TREES]: Initialised tree module"); |
83 | } | 202 | } |
84 | 203 | ||
85 | public void PostInitialise() | 204 | public void PostInitialise() |
86 | { | 205 | { |
206 | ReloadCopse(); | ||
207 | if (m_copse.Count > 0) | ||
208 | m_log.Info("[TREES]: Copse load complete"); | ||
209 | |||
210 | if (m_active_trees) | ||
211 | activeizeTreeze(true); | ||
87 | } | 212 | } |
88 | 213 | ||
89 | public void Close() | 214 | public void Close() |
@@ -102,61 +227,397 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator | |||
102 | 227 | ||
103 | #endregion | 228 | #endregion |
104 | 229 | ||
105 | /// <summary> | 230 | //-------------------------------------------------------------- |
106 | /// Handle a tree command from the console. | 231 | |
107 | /// </summary> | 232 | #region ICommandableModule Members |
108 | /// <param name="module"></param> | 233 | |
109 | /// <param name="cmdparams"></param> | 234 | private void HandleTreeActive(Object[] args) |
110 | public void HandleTreeConsoleCommand(string module, string[] cmdparams) | ||
111 | { | 235 | { |
112 | if (m_scene.ConsoleScene() != null && m_scene.ConsoleScene() != m_scene) | 236 | if ((Boolean)args[0] && !m_active_trees) |
113 | return; | 237 | { |
238 | m_log.InfoFormat("[TREES]: Activating Trees"); | ||
239 | m_active_trees = true; | ||
240 | activeizeTreeze(m_active_trees); | ||
241 | } | ||
242 | else if (!(Boolean)args[0] && m_active_trees) | ||
243 | { | ||
244 | m_log.InfoFormat("[TREES]: Trees module is no longer active"); | ||
245 | m_active_trees = false; | ||
246 | activeizeTreeze(m_active_trees); | ||
247 | } | ||
248 | else | ||
249 | { | ||
250 | m_log.InfoFormat("[TREES]: Trees module is already in the required state"); | ||
251 | } | ||
252 | } | ||
114 | 253 | ||
115 | if (cmdparams[1] == "active") | 254 | private void HandleTreeFreeze(Object[] args) |
255 | { | ||
256 | string copsename = ((string)args[0]).Trim(); | ||
257 | Boolean freezeState = (Boolean) args[1]; | ||
258 | |||
259 | foreach (Copse cp in m_copse) | ||
116 | { | 260 | { |
117 | if (cmdparams.Length <= 2) | 261 | if (cp.m_name == copsename && (!cp.m_frozen && freezeState || cp.m_frozen && !freezeState)) |
118 | { | 262 | { |
119 | if (m_active_trees) | 263 | cp.m_frozen = freezeState; |
120 | m_log.InfoFormat("[TREES]: Trees are currently active"); | 264 | foreach (UUID tree in cp.m_trees) |
121 | else | 265 | { |
122 | m_log.InfoFormat("[TREES]: Trees are currently not active"); | 266 | SceneObjectPart sop = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart; |
267 | sop.Name = (freezeState ? sop.Name.Replace("ATPM", "FTPM") : sop.Name.Replace("FTPM", "ATPM")); | ||
268 | sop.ParentGroup.HasGroupChanged = true; | ||
269 | } | ||
270 | |||
271 | m_log.InfoFormat("[TREES]: Activity for copse {0} is frozen {1}", copsename, freezeState); | ||
272 | return; | ||
123 | } | 273 | } |
124 | else if (cmdparams[2] == "true" && !m_active_trees) | 274 | else if (cp.m_name == copsename && (cp.m_frozen && freezeState || !cp.m_frozen && !freezeState)) |
125 | { | 275 | { |
126 | m_log.InfoFormat("[TREES]: Activating Trees"); | 276 | m_log.InfoFormat("[TREES]: Copse {0} is already in the requested freeze state", copsename); |
127 | m_active_trees = true; | 277 | return; |
128 | activeizeTreeze(m_active_trees); | ||
129 | } | 278 | } |
130 | else if (cmdparams[2] == "false" && m_active_trees) | 279 | } |
280 | m_log.InfoFormat("[TREES]: Copse {0} was not found - command failed", copsename); | ||
281 | } | ||
282 | |||
283 | private void HandleTreeLoad(Object[] args) | ||
284 | { | ||
285 | Copse copse; | ||
286 | |||
287 | m_log.InfoFormat("[TREES]: Loading copse definition...."); | ||
288 | |||
289 | copse = new Copse(((string)args[0]), false); | ||
290 | foreach (Copse cp in m_copse) | ||
291 | { | ||
292 | if (cp.m_name == copse.m_name) | ||
131 | { | 293 | { |
132 | m_log.InfoFormat("[TREES]: Trees no longer active, for now..."); | 294 | m_log.InfoFormat("[TREES]: Copse: {0} is already defined - command failed", copse.m_name); |
133 | m_active_trees = false; | 295 | return; |
134 | activeizeTreeze(m_active_trees); | ||
135 | } | 296 | } |
136 | else | 297 | } |
298 | |||
299 | m_copse.Add(copse); | ||
300 | m_log.InfoFormat("[TREES]: Loaded copse: {0}", copse.ToString()); | ||
301 | } | ||
302 | |||
303 | private void HandleTreePlant(Object[] args) | ||
304 | { | ||
305 | string copsename = ((string)args[0]).Trim(); | ||
306 | |||
307 | m_log.InfoFormat("[TREES]: New tree planting for copse {0}", copsename); | ||
308 | UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner; | ||
309 | if (uuid == UUID.Zero) | ||
310 | uuid = m_scene.RegionInfo.MasterAvatarAssignedUUID; | ||
311 | |||
312 | foreach (Copse copse in m_copse) | ||
313 | { | ||
314 | if (copse.m_name == copsename) | ||
137 | { | 315 | { |
138 | m_log.InfoFormat("[TREES]: When setting the tree module active via the console, you must specify true or false"); | 316 | if (!copse.m_planted) |
317 | { | ||
318 | // The first tree for a copse is created here | ||
319 | CreateTree(uuid, copse, copse.m_seed_point); | ||
320 | copse.m_planted = true; | ||
321 | return; | ||
322 | } | ||
323 | else | ||
324 | { | ||
325 | m_log.InfoFormat("[TREES]: Copse {0} has already been planted", copsename); | ||
326 | } | ||
139 | } | 327 | } |
140 | } | 328 | } |
141 | else if (cmdparams[1] == "plant") | 329 | m_log.InfoFormat("[TREES]: Copse {0} not found for planting", copsename); |
330 | } | ||
331 | |||
332 | private void HandleTreeRate(Object[] args) | ||
333 | { | ||
334 | m_update_ms = (double)args[0]; | ||
335 | if (m_update_ms >= 1000.0) | ||
142 | { | 336 | { |
143 | m_log.InfoFormat("[TREES]: New tree planting"); | 337 | if (m_active_trees) |
144 | UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner; | 338 | { |
145 | if (uuid == UUID.Zero) | 339 | activeizeTreeze(false); |
146 | uuid = m_scene.RegionInfo.MasterAvatarAssignedUUID; | 340 | activeizeTreeze(true); |
147 | CreateTree(uuid, new Vector3(128.0f, 128.0f, 0.0f)); | 341 | } |
342 | m_log.InfoFormat("[TREES]: Update rate set to {0} mSec", m_update_ms); | ||
148 | } | 343 | } |
149 | else | 344 | else |
150 | { | 345 | { |
151 | m_log.InfoFormat("[TREES]: Unknown command"); | 346 | m_log.InfoFormat("[TREES]: minimum rate is 1000.0 mSec - command failed"); |
152 | } | 347 | } |
153 | } | 348 | } |
154 | 349 | ||
350 | private void HandleTreeReload(Object[] args) | ||
351 | { | ||
352 | if (m_active_trees) | ||
353 | { | ||
354 | CalculateTrees.Stop(); | ||
355 | } | ||
356 | |||
357 | ReloadCopse(); | ||
358 | |||
359 | if (m_active_trees) | ||
360 | { | ||
361 | CalculateTrees.Start(); | ||
362 | } | ||
363 | } | ||
364 | |||
365 | private void HandleTreeRemove(Object[] args) | ||
366 | { | ||
367 | string copsename = ((string)args[0]).Trim(); | ||
368 | Copse copseIdentity = null; | ||
369 | |||
370 | foreach (Copse cp in m_copse) | ||
371 | { | ||
372 | if (cp.m_name == copsename) | ||
373 | { | ||
374 | copseIdentity = cp; | ||
375 | } | ||
376 | } | ||
377 | |||
378 | if (copseIdentity != null) | ||
379 | { | ||
380 | foreach (UUID tree in copseIdentity.m_trees) | ||
381 | { | ||
382 | if (m_scene.Entities.ContainsKey(tree)) | ||
383 | { | ||
384 | SceneObjectPart selectedTree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart; | ||
385 | |||
386 | |||
387 | m_scene.DeleteSceneObject(selectedTree.ParentGroup, false); | ||
388 | m_scene.ForEachClient(delegate(IClientAPI controller) | ||
389 | { | ||
390 | controller.SendKillObject(m_scene.RegionInfo.RegionHandle, | ||
391 | selectedTree.LocalId); | ||
392 | }); | ||
393 | } | ||
394 | else | ||
395 | { | ||
396 | m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree); | ||
397 | } | ||
398 | } | ||
399 | copseIdentity.m_trees = new List<UUID>(); | ||
400 | m_copse.Remove(copseIdentity); | ||
401 | m_log.InfoFormat("[TREES]: Copse {0} has been removed", copsename); | ||
402 | } | ||
403 | else | ||
404 | { | ||
405 | m_log.InfoFormat("[TREES]: Copse {0} was not found - command failed", copsename); | ||
406 | } | ||
407 | } | ||
408 | |||
409 | private void HandleTreeStatistics(Object[] args) | ||
410 | { | ||
411 | m_log.InfoFormat("[TREES]: Activity State: {0}; Update Rate: {1}", m_active_trees, m_update_ms); | ||
412 | foreach (Copse cp in m_copse) | ||
413 | { | ||
414 | m_log.InfoFormat("[TREES]: Copse {0}; {1} trees; frozen {2}", cp.m_name, cp.m_trees.Count, cp.m_frozen); | ||
415 | } | ||
416 | } | ||
417 | |||
418 | private void InstallCommands() | ||
419 | { | ||
420 | Command treeActiveCommand = | ||
421 | new Command("active", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeActive, "Change activity state for the trees module"); | ||
422 | treeActiveCommand.AddArgument("activeTF", "The required activity state", "Boolean"); | ||
423 | |||
424 | Command treeFreezeCommand = | ||
425 | new Command("freeze", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeFreeze, "Freeze/Unfreeze activity for a defined copse"); | ||
426 | treeFreezeCommand.AddArgument("copse", "The required copse", "String"); | ||
427 | treeFreezeCommand.AddArgument("freezeTF", "The required freeze state", "Boolean"); | ||
428 | |||
429 | Command treeLoadCommand = | ||
430 | new Command("load", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeLoad, "Load a copse definition from an xml file"); | ||
431 | treeLoadCommand.AddArgument("filename", "The (xml) file you wish to load", "String"); | ||
432 | |||
433 | Command treePlantCommand = | ||
434 | new Command("plant", CommandIntentions.COMMAND_HAZARDOUS, HandleTreePlant, "Start the planting on a copse"); | ||
435 | treePlantCommand.AddArgument("copse", "The required copse", "String"); | ||
436 | |||
437 | Command treeRateCommand = | ||
438 | new Command("rate", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeRate, "Reset the tree update rate (mSec)"); | ||
439 | treeRateCommand.AddArgument("updateRate", "The required update rate (minimum 1000.0)", "Double"); | ||
440 | |||
441 | Command treeReloadCommand = | ||
442 | new Command("reload", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeReload, "Reload copse definitions from the in-scene trees"); | ||
443 | |||
444 | Command treeRemoveCommand = | ||
445 | new Command("remove", CommandIntentions.COMMAND_HAZARDOUS, HandleTreeRemove, "Remove a copse definition and all its in-scene trees"); | ||
446 | treeRemoveCommand.AddArgument("copse", "The required copse", "String"); | ||
447 | |||
448 | Command treeStatisticsCommand = | ||
449 | new Command("statistics", CommandIntentions.COMMAND_STATISTICAL, HandleTreeStatistics, "Log statistics about the trees"); | ||
450 | |||
451 | m_commander.RegisterCommand("active", treeActiveCommand); | ||
452 | m_commander.RegisterCommand("freeze", treeFreezeCommand); | ||
453 | m_commander.RegisterCommand("load", treeLoadCommand); | ||
454 | m_commander.RegisterCommand("plant", treePlantCommand); | ||
455 | m_commander.RegisterCommand("rate", treeRateCommand); | ||
456 | m_commander.RegisterCommand("reload", treeReloadCommand); | ||
457 | m_commander.RegisterCommand("remove", treeRemoveCommand); | ||
458 | m_commander.RegisterCommand("statistics", treeStatisticsCommand); | ||
459 | |||
460 | m_scene.RegisterModuleCommander(m_commander); | ||
461 | } | ||
462 | |||
463 | /// <summary> | ||
464 | /// Processes commandline input. Do not call directly. | ||
465 | /// </summary> | ||
466 | /// <param name="args">Commandline arguments</param> | ||
467 | private void EventManager_OnPluginConsole(string[] args) | ||
468 | { | ||
469 | if (args[0] == "tree") | ||
470 | { | ||
471 | if (args.Length == 1) | ||
472 | { | ||
473 | m_commander.ProcessConsoleCommand("help", new string[0]); | ||
474 | return; | ||
475 | } | ||
476 | |||
477 | string[] tmpArgs = new string[args.Length - 2]; | ||
478 | int i; | ||
479 | for (i = 2; i < args.Length; i++) | ||
480 | { | ||
481 | tmpArgs[i - 2] = args[i]; | ||
482 | } | ||
483 | |||
484 | m_commander.ProcessConsoleCommand(args[1], tmpArgs); | ||
485 | } | ||
486 | } | ||
487 | #endregion | ||
488 | |||
489 | #region IVegetationModule Members | ||
490 | |||
491 | public SceneObjectGroup AddTree( | ||
492 | UUID uuid, UUID groupID, Vector3 scale, Quaternion rotation, Vector3 position, Tree treeType, bool newTree) | ||
493 | { | ||
494 | PrimitiveBaseShape treeShape = new PrimitiveBaseShape(); | ||
495 | treeShape.PathCurve = 16; | ||
496 | treeShape.PathEnd = 49900; | ||
497 | treeShape.PCode = newTree ? (byte)PCode.NewTree : (byte)PCode.Tree; | ||
498 | treeShape.Scale = scale; | ||
499 | treeShape.State = (byte)treeType; | ||
500 | |||
501 | return m_scene.AddNewPrim(uuid, groupID, position, rotation, treeShape); | ||
502 | } | ||
503 | |||
504 | #endregion | ||
505 | |||
506 | #region IEntityCreator Members | ||
507 | |||
508 | protected static readonly PCode[] creationCapabilities = new PCode[] { PCode.NewTree, PCode.Tree }; | ||
509 | public PCode[] CreationCapabilities { get { return creationCapabilities; } } | ||
510 | |||
511 | public SceneObjectGroup CreateEntity( | ||
512 | UUID ownerID, UUID groupID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape) | ||
513 | { | ||
514 | if (Array.IndexOf(creationCapabilities, (PCode)shape.PCode) < 0) | ||
515 | { | ||
516 | m_log.DebugFormat("[VEGETATION]: PCode {0} not handled by {1}", shape.PCode, Name); | ||
517 | return null; | ||
518 | } | ||
519 | |||
520 | SceneObjectGroup sceneObject = new SceneObjectGroup(ownerID, pos, rot, shape); | ||
521 | SceneObjectPart rootPart = sceneObject.GetChildPart(sceneObject.UUID); | ||
522 | |||
523 | rootPart.AddFlag(PrimFlags.Phantom); | ||
524 | |||
525 | m_scene.AddNewSceneObject(sceneObject, true); | ||
526 | sceneObject.SetGroup(groupID, null); | ||
527 | |||
528 | return sceneObject; | ||
529 | } | ||
530 | |||
531 | #endregion | ||
532 | |||
533 | //-------------------------------------------------------------- | ||
534 | |||
535 | #region Tree Utilities | ||
536 | static public void SerializeObject(string fileName, Object obj) | ||
537 | { | ||
538 | try | ||
539 | { | ||
540 | XmlSerializer xs = new XmlSerializer(typeof(Copse)); | ||
541 | |||
542 | using (XmlTextWriter writer = new XmlTextWriter(fileName, System.Text.Encoding.UTF8)) | ||
543 | { | ||
544 | writer.Formatting = Formatting.Indented; | ||
545 | xs.Serialize(writer, obj); | ||
546 | } | ||
547 | } | ||
548 | catch (SystemException ex) | ||
549 | { | ||
550 | throw new ApplicationException("Unexpected failure in Tree serialization", ex); | ||
551 | } | ||
552 | } | ||
553 | |||
554 | static public object DeserializeObject(string fileName) | ||
555 | { | ||
556 | try | ||
557 | { | ||
558 | XmlSerializer xs = new XmlSerializer(typeof(Copse)); | ||
559 | |||
560 | using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) | ||
561 | return xs.Deserialize(fs); | ||
562 | } | ||
563 | catch (SystemException ex) | ||
564 | { | ||
565 | throw new ApplicationException("Unexpected failure in Tree de-serialization", ex); | ||
566 | } | ||
567 | } | ||
568 | |||
569 | private void ReloadCopse() | ||
570 | { | ||
571 | m_copse = new List<Copse>(); | ||
572 | |||
573 | List<EntityBase> objs = m_scene.GetEntities(); | ||
574 | |||
575 | foreach (EntityBase obj in objs) | ||
576 | { | ||
577 | if (obj is SceneObjectGroup) | ||
578 | { | ||
579 | SceneObjectGroup grp = (SceneObjectGroup)obj; | ||
580 | |||
581 | if (grp.Name.Length > 5 && (grp.Name.Substring(0, 5) == "ATPM:" || grp.Name.Substring(0, 5) == "FTPM:")) | ||
582 | { | ||
583 | // Create a new copse definition or add uuid to an existing definition | ||
584 | try | ||
585 | { | ||
586 | Boolean copsefound = false; | ||
587 | Copse copse = new Copse(grp.Name); | ||
588 | |||
589 | foreach (Copse cp in m_copse) | ||
590 | { | ||
591 | if (cp.m_name == copse.m_name) | ||
592 | { | ||
593 | copsefound = true; | ||
594 | cp.m_trees.Add(grp.UUID); | ||
595 | //m_log.DebugFormat("[TREES]: Found tree {0}", grp.UUID); | ||
596 | } | ||
597 | } | ||
598 | |||
599 | if (!copsefound) | ||
600 | { | ||
601 | m_log.InfoFormat("[TREES]: Found copse {0}", grp.Name); | ||
602 | m_copse.Add(copse); | ||
603 | copse.m_trees.Add(grp.UUID); | ||
604 | } | ||
605 | } | ||
606 | catch | ||
607 | { | ||
608 | m_log.InfoFormat("[TREES]: Ill formed copse definition {0} - ignoring", grp.Name); | ||
609 | } | ||
610 | } | ||
611 | } | ||
612 | } | ||
613 | } | ||
614 | #endregion | ||
615 | |||
155 | private void activeizeTreeze(bool activeYN) | 616 | private void activeizeTreeze(bool activeYN) |
156 | { | 617 | { |
157 | if (activeYN) | 618 | if (activeYN) |
158 | { | 619 | { |
159 | CalculateTrees = new Timer(m_tree_updates); | 620 | CalculateTrees = new Timer(m_update_ms); |
160 | CalculateTrees.Elapsed += CalculateTrees_Elapsed; | 621 | CalculateTrees.Elapsed += CalculateTrees_Elapsed; |
161 | CalculateTrees.Start(); | 622 | CalculateTrees.Start(); |
162 | } | 623 | } |
@@ -168,143 +629,156 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator | |||
168 | 629 | ||
169 | private void growTrees() | 630 | private void growTrees() |
170 | { | 631 | { |
171 | foreach (UUID tree in m_trees) | 632 | foreach (Copse copse in m_copse) |
172 | { | 633 | { |
173 | if (m_scene.Entities.ContainsKey(tree)) | 634 | if (!copse.m_frozen) |
174 | { | ||
175 | SceneObjectPart s_tree = ((SceneObjectGroup) m_scene.Entities[tree]).RootPart; | ||
176 | |||
177 | // 100 seconds to grow 1m | ||
178 | s_tree.Scale += new Vector3(0.1f, 0.1f, 0.1f); | ||
179 | s_tree.SendFullUpdateToAllClients(); | ||
180 | //s_tree.ScheduleTerseUpdate(); | ||
181 | } | ||
182 | else | ||
183 | { | 635 | { |
184 | m_trees.Remove(tree); | 636 | foreach (UUID tree in copse.m_trees) |
637 | { | ||
638 | if (m_scene.Entities.ContainsKey(tree)) | ||
639 | { | ||
640 | SceneObjectPart s_tree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart; | ||
641 | |||
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) | ||
643 | { | ||
644 | s_tree.Scale += copse.m_rate; | ||
645 | s_tree.ParentGroup.HasGroupChanged = true; | ||
646 | s_tree.ScheduleFullUpdate(); | ||
647 | } | ||
648 | } | ||
649 | else | ||
650 | { | ||
651 | m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree); | ||
652 | } | ||
653 | } | ||
185 | } | 654 | } |
186 | } | 655 | } |
187 | } | 656 | } |
188 | 657 | ||
189 | private void seedTrees() | 658 | private void seedTrees() |
190 | { | 659 | { |
191 | foreach (UUID tree in m_trees) | 660 | foreach (Copse copse in m_copse) |
192 | { | 661 | { |
193 | if (m_scene.Entities.ContainsKey(tree)) | 662 | if (!copse.m_frozen) |
194 | { | 663 | { |
195 | SceneObjectPart s_tree = ((SceneObjectGroup) m_scene.Entities[tree]).RootPart; | 664 | foreach (UUID tree in copse.m_trees) |
196 | |||
197 | if (s_tree.Scale.X > 0.5) | ||
198 | { | 665 | { |
199 | if (Util.RandomClass.NextDouble() > 0.75) | 666 | if (m_scene.Entities.ContainsKey(tree)) |
200 | { | 667 | { |
201 | SpawnChild(s_tree); | 668 | SceneObjectPart s_tree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart; |
669 | |||
670 | if (copse.m_trees.Count < copse.m_tree_quantity) | ||
671 | { | ||
672 | // Tree has grown enough to seed if it has grown by at least 25% of seeded to full grown height | ||
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); | ||
202 | } | 685 | } |
203 | } | 686 | } |
204 | } | 687 | } |
205 | else | ||
206 | { | ||
207 | m_trees.Remove(tree); | ||
208 | } | ||
209 | } | 688 | } |
210 | } | 689 | } |
211 | 690 | ||
212 | private void killTrees() | 691 | private void killTrees() |
213 | { | 692 | { |
214 | foreach (UUID tree in m_trees) | 693 | foreach (Copse copse in m_copse) |
215 | { | 694 | { |
216 | double killLikelyhood = 0.0; | 695 | if (!copse.m_frozen && copse.m_trees.Count >= copse.m_tree_quantity) |
217 | |||
218 | if (m_scene.Entities.ContainsKey(tree)) | ||
219 | { | 696 | { |
220 | SceneObjectPart selectedTree = ((SceneObjectGroup) m_scene.Entities[tree]).RootPart; | 697 | foreach (UUID tree in copse.m_trees) |
221 | double selectedTreeScale = Math.Sqrt(Math.Pow(selectedTree.Scale.X, 2) + | ||
222 | Math.Pow(selectedTree.Scale.Y, 2) + | ||
223 | Math.Pow(selectedTree.Scale.Z, 2)); | ||
224 | |||
225 | foreach (UUID picktree in m_trees) | ||
226 | { | 698 | { |
227 | if (picktree != tree) | 699 | double killLikelyhood = 0.0; |
700 | |||
701 | if (m_scene.Entities.ContainsKey(tree)) | ||
228 | { | 702 | { |
229 | SceneObjectPart pickedTree = ((SceneObjectGroup) m_scene.Entities[picktree]).RootPart; | 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)); | ||
230 | 707 | ||
231 | double pickedTreeScale = Math.Sqrt(Math.Pow(pickedTree.Scale.X, 2) + | 708 | foreach (UUID picktree in copse.m_trees) |
232 | Math.Pow(pickedTree.Scale.Y, 2) + | 709 | { |
233 | Math.Pow(pickedTree.Scale.Z, 2)); | 710 | if (picktree != tree) |
711 | { | ||
712 | SceneObjectPart pickedTree = ((SceneObjectGroup)m_scene.Entities[picktree]).RootPart; | ||
234 | 713 | ||
235 | double pickedTreeDistance = Math.Sqrt(Math.Pow(Math.Abs(pickedTree.AbsolutePosition.X - selectedTree.AbsolutePosition.X), 2) + | 714 | double pickedTreeScale = Math.Sqrt(Math.Pow(pickedTree.Scale.X, 2) + |
236 | Math.Pow(Math.Abs(pickedTree.AbsolutePosition.Y - selectedTree.AbsolutePosition.Y), 2) + | 715 | Math.Pow(pickedTree.Scale.Y, 2) + |
237 | Math.Pow(Math.Abs(pickedTree.AbsolutePosition.Z - selectedTree.AbsolutePosition.Z), 2)); | 716 | Math.Pow(pickedTree.Scale.Z, 2)); |
238 | 717 | ||
239 | killLikelyhood += (selectedTreeScale / (pickedTreeScale * pickedTreeDistance)) * 0.1; | 718 | double pickedTreeDistance = Vector3.Distance(pickedTree.AbsolutePosition, selectedTree.AbsolutePosition); |
240 | } | ||
241 | } | ||
242 | 719 | ||
243 | if (Util.RandomClass.NextDouble() < killLikelyhood) | 720 | killLikelyhood += (selectedTreeScale / (pickedTreeScale * pickedTreeDistance)) * 0.1; |
244 | { | 721 | } |
245 | m_scene.DeleteSceneObject(selectedTree.ParentGroup, false); | 722 | } |
246 | m_trees.Remove(selectedTree.ParentGroup.UUID); | ||
247 | 723 | ||
248 | m_scene.ForEachClient(delegate(IClientAPI controller) | 724 | if (Util.RandomClass.NextDouble() < killLikelyhood) |
249 | { | 725 | { |
250 | controller.SendKillObject(m_scene.RegionInfo.RegionHandle, | ||
251 | selectedTree.LocalId); | ||
252 | }); | ||
253 | 726 | ||
254 | break; | 727 | m_scene.DeleteSceneObject(selectedTree.ParentGroup, false); |
728 | copse.m_trees.Remove(selectedTree.ParentGroup.UUID); | ||
729 | |||
730 | m_scene.ForEachClient(delegate(IClientAPI controller) | ||
731 | { | ||
732 | controller.SendKillObject(m_scene.RegionInfo.RegionHandle, | ||
733 | selectedTree.LocalId); | ||
734 | }); | ||
735 | |||
736 | break; | ||
737 | } | ||
738 | } | ||
739 | else | ||
740 | { | ||
741 | m_log.DebugFormat("[TREES]: Tree not in scene {0}", tree); | ||
742 | } | ||
255 | } | 743 | } |
256 | selectedTree.SetText(killLikelyhood.ToString(), new Vector3(1.0f, 1.0f, 1.0f), 1.0); | ||
257 | } | ||
258 | else | ||
259 | { | ||
260 | m_trees.Remove(tree); | ||
261 | } | 744 | } |
262 | } | 745 | } |
263 | } | 746 | } |
264 | 747 | ||
265 | private void SpawnChild(SceneObjectPart s_tree) | 748 | private void SpawnChild(Copse copse, SceneObjectPart s_tree) |
266 | { | 749 | { |
267 | Vector3 position = new Vector3(); | 750 | Vector3 position = new Vector3(); |
268 | 751 | ||
269 | position.X = s_tree.AbsolutePosition.X + (1 * (-1 * Util.RandomClass.Next(1))); | ||
270 | if (position.X > 255) | ||
271 | position.X = 255; | ||
272 | if (position.X < 0) | ||
273 | position.X = 0; | ||
274 | position.Y = s_tree.AbsolutePosition.Y + (1 * (-1 * Util.RandomClass.Next(1))); | ||
275 | if (position.Y > 255) | ||
276 | position.Y = 255; | ||
277 | if (position.Y < 0) | ||
278 | position.Y = 0; | ||
279 | |||
280 | double randX = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3); | 752 | double randX = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3); |
281 | double randY = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3); | 753 | double randY = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3); |
282 | 754 | ||
283 | position.X += (float) randX; | 755 | position.X = s_tree.AbsolutePosition.X + (float)randX; |
284 | position.Y += (float) randY; | 756 | position.Y = s_tree.AbsolutePosition.Y + (float)randY; |
285 | 757 | ||
286 | UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner; | 758 | if (position.X <= 255 && position.X >= 0 && |
287 | if (uuid == UUID.Zero) | 759 | position.Y <= 255 && position.Y >= 0 && |
288 | uuid = m_scene.RegionInfo.MasterAvatarAssignedUUID; | 760 | Util.GetDistanceTo(position, copse.m_seed_point) <= copse.m_range) |
761 | { | ||
762 | UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner; | ||
763 | if (uuid == UUID.Zero) | ||
764 | uuid = m_scene.RegionInfo.MasterAvatarAssignedUUID; | ||
289 | 765 | ||
290 | CreateTree(uuid, position); | 766 | CreateTree(uuid, copse, position); |
767 | } | ||
291 | } | 768 | } |
292 | 769 | ||
293 | private void CreateTree(UUID uuid, Vector3 position) | 770 | private void CreateTree(UUID uuid, Copse copse, Vector3 position) |
294 | { | 771 | { |
295 | position.Z = (float) m_scene.Heightmap[(int) position.X, (int) position.Y]; | ||
296 | 772 | ||
297 | IVegetationModule module = m_scene.RequestModuleInterface<IVegetationModule>(); | 773 | position.Z = (float)m_scene.Heightmap[(int)position.X, (int)position.Y]; |
298 | 774 | if (position.Z >= copse.m_treeline_low && position.Z <= copse.m_treeline_high) | |
299 | if (null == module) | 775 | { |
300 | return; | 776 | SceneObjectGroup tree = AddTree(uuid, UUID.Zero, copse.m_initial_scale, Quaternion.Identity, position, copse.m_tree_type, false); |
301 | 777 | ||
302 | SceneObjectGroup tree | 778 | tree.Name = copse.ToString(); |
303 | = module.AddTree( | 779 | copse.m_trees.Add(tree.UUID); |
304 | uuid, UUID.Zero, new Vector3(0.1f, 0.1f, 0.1f), Quaternion.Identity, position, Tree.Cypress1, false); | 780 | tree.SendGroupFullUpdate(); |
305 | 781 | } | |
306 | m_trees.Add(tree.UUID); | ||
307 | tree.SendGroupFullUpdate(); | ||
308 | } | 782 | } |
309 | 783 | ||
310 | private void CalculateTrees_Elapsed(object sender, ElapsedEventArgs e) | 784 | private void CalculateTrees_Elapsed(object sender, ElapsedEventArgs e) |
@@ -315,3 +789,4 @@ namespace OpenSim.Region.OptionalModules.World.TreePopulator | |||
315 | } | 789 | } |
316 | } | 790 | } |
317 | } | 791 | } |
792 | |||