29 using System.Collections.Generic;
30 using System.Reflection;
36 using OpenSim.Framework;
37 using OpenSim.Region.CoreModules.Framework.InterfaceCommander;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.Framework.Scenes;
42 using System.Xml.Serialization;
50 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"TreePopulatorModule")]
53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55 private Scene m_scene;
57 [XmlRootAttribute(ElementName =
"Copse", IsNullable =
false)]
81 public Copse(
string fileName, Boolean planted)
85 this.m_name = cp.m_name;
86 this.m_frozen = cp.m_frozen;
87 this.m_tree_quantity = cp.m_tree_quantity;
88 this.m_treeline_high = cp.m_treeline_high;
89 this.m_treeline_low = cp.m_treeline_low;
90 this.m_range = cp.m_range;
91 this.m_tree_type = cp.m_tree_type;
92 this.m_seed_point = cp.m_seed_point;
93 this.m_initial_scale = cp.m_initial_scale;
94 this.m_maximum_scale = cp.m_maximum_scale;
95 this.m_initial_scale = cp.m_initial_scale;
96 this.m_rate = cp.m_rate;
97 this.m_planted = planted;
98 this.m_trees =
new List<UUID>();
103 char[] delimiterChars = {
':',
';'};
104 string[] field = copsedef.Split(delimiterChars);
106 this.m_name = field[1].Trim();
107 this.m_frozen = (copsedef[0] ==
'F');
108 this.m_tree_quantity = int.Parse(field[2]);
109 this.m_treeline_high = float.Parse(field[3], Culture.NumberFormatInfo);
110 this.m_treeline_low = float.Parse(field[4], Culture.NumberFormatInfo);
111 this.m_range = double.Parse(field[5], Culture.NumberFormatInfo);
112 this.m_tree_type = (
Tree) Enum.Parse(typeof(
Tree),field[6]);
113 this.m_seed_point = Vector3.Parse(field[7]);
114 this.m_initial_scale = Vector3.Parse(field[8]);
115 this.m_maximum_scale = Vector3.Parse(field[9]);
116 this.m_rate = Vector3.Parse(field[10]);
117 this.m_planted =
true;
118 this.m_trees =
new List<UUID>();
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 this.m_frozen =
false;
125 this.m_tree_quantity = quantity;
126 this.m_treeline_high = high;
127 this.m_treeline_low = low;
128 this.m_range = range;
129 this.m_tree_type = type;
130 this.m_seed_point = point;
131 this.m_initial_scale = scale;
132 this.m_maximum_scale = max_scale;
134 this.m_planted =
false;
135 this.m_trees = trees;
140 string frozen = (this.m_frozen ?
"F" :
"A");
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 this.m_tree_quantity,
146 this.m_treeline_high,
150 this.m_seed_point.ToString(),
151 this.m_initial_scale.ToString(),
152 this.m_maximum_scale.ToString(),
153 this.m_rate.ToString());
157 private List<Copse> m_copse;
159 private double m_update_ms = 1000.0;
160 private bool m_active_trees =
false;
162 Timer CalculateTrees;
164 #region ICommandableModule Members
168 get {
return m_commander; }
173 #region Region Module interface
181 m_active_trees = config.Configs[
"Trees"].GetBoolean(
"active_trees", m_active_trees);
185 m_log.Debug(
"[TREES]: ini failure for active_trees - using default");
190 m_update_ms = config.Configs[
"Trees"].GetDouble(
"update_rate", m_update_ms);
194 m_log.Debug(
"[TREES]: ini failure for update_rate - using default");
199 m_log.Debug(
"[TREES]: Initialised tree module");
205 m_scene.RegisterModuleCommander(m_commander);
206 m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole;
217 if (m_copse.Count > 0)
218 m_log.Info(
"[TREES]: Copse load complete");
221 activeizeTreeze(
true);
230 get {
return "TreePopulatorModule"; }
233 public Type ReplaceableInterface
243 #region ICommandableModule Members
245 private void HandleTreeActive(Object[] args)
247 if ((Boolean)args[0] && !m_active_trees)
249 m_log.InfoFormat(
"[TREES]: Activating Trees");
250 m_active_trees =
true;
251 activeizeTreeze(m_active_trees);
253 else if (!(Boolean)args[0] && m_active_trees)
255 m_log.InfoFormat(
"[TREES]: Trees module is no longer active");
256 m_active_trees =
false;
257 activeizeTreeze(m_active_trees);
261 m_log.InfoFormat(
"[TREES]: Trees module is already in the required state");
265 private void HandleTreeFreeze(Object[] args)
267 string copsename = ((string)args[0]).Trim();
270 foreach (Copse cp
in m_copse)
272 if (cp.m_name == copsename && (!cp.m_frozen && freezeState || cp.m_frozen && !freezeState))
274 cp.m_frozen = freezeState;
275 foreach (UUID tree
in cp.m_trees)
278 sop.Name = (freezeState ? sop.Name.Replace(
"ATPM",
"FTPM") : sop.
Name.Replace(
"FTPM",
"ATPM"));
279 sop.ParentGroup.HasGroupChanged =
true;
282 m_log.InfoFormat(
"[TREES]: Activity for copse {0} is frozen {1}", copsename, freezeState);
285 else if (cp.m_name == copsename && (cp.m_frozen && freezeState || !cp.m_frozen && !freezeState))
287 m_log.InfoFormat(
"[TREES]: Copse {0} is already in the requested freeze state", copsename);
291 m_log.InfoFormat(
"[TREES]: Copse {0} was not found - command failed", copsename);
294 private void HandleTreeLoad(Object[] args)
298 m_log.InfoFormat(
"[TREES]: Loading copse definition....");
300 copse =
new Copse(((
string)args[0]),
false);
301 foreach (Copse cp
in m_copse)
303 if (cp.m_name == copse.m_name)
305 m_log.InfoFormat(
"[TREES]: Copse: {0} is already defined - command failed", copse.m_name);
311 m_log.InfoFormat(
"[TREES]: Loaded copse: {0}", copse.ToString());
314 private void HandleTreePlant(Object[] args)
316 string copsename = ((string)args[0]).Trim();
318 m_log.InfoFormat(
"[TREES]: New tree planting for copse {0}", copsename);
319 UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner;
321 foreach (Copse copse
in m_copse)
323 if (copse.m_name == copsename)
325 if (!copse.m_planted)
328 CreateTree(uuid, copse, copse.m_seed_point);
329 copse.m_planted =
true;
334 m_log.InfoFormat(
"[TREES]: Copse {0} has already been planted", copsename);
338 m_log.InfoFormat(
"[TREES]: Copse {0} not found for planting", copsename);
341 private void HandleTreeRate(Object[] args)
343 m_update_ms = (double)args[0];
344 if (m_update_ms >= 1000.0)
348 activeizeTreeze(
false);
349 activeizeTreeze(
true);
351 m_log.InfoFormat(
"[TREES]: Update rate set to {0} mSec", m_update_ms);
355 m_log.InfoFormat(
"[TREES]: minimum rate is 1000.0 mSec - command failed");
359 private void HandleTreeReload(Object[] args)
363 CalculateTrees.Stop();
370 CalculateTrees.Start();
374 private void HandleTreeRemove(Object[] args)
376 string copsename = ((string)args[0]).Trim();
377 Copse copseIdentity = null;
379 foreach (Copse cp
in m_copse)
381 if (cp.m_name == copsename)
387 if (copseIdentity != null)
389 foreach (UUID tree
in copseIdentity.m_trees)
391 if (m_scene.Entities.ContainsKey(tree))
395 m_scene.DeleteSceneObject(selectedTree.ParentGroup,
false);
399 m_log.DebugFormat(
"[TREES]: Tree not in scene {0}", tree);
402 copseIdentity.m_trees =
new List<UUID>();
403 m_copse.Remove(copseIdentity);
404 m_log.InfoFormat(
"[TREES]: Copse {0} has been removed", copsename);
408 m_log.InfoFormat(
"[TREES]: Copse {0} was not found - command failed", copsename);
412 private void HandleTreeStatistics(Object[] args)
414 m_log.InfoFormat(
"[TREES]: Activity State: {0}; Update Rate: {1}", m_active_trees, m_update_ms);
415 foreach (Copse cp
in m_copse)
417 m_log.InfoFormat(
"[TREES]: Copse {0}; {1} trees; frozen {2}", cp.m_name, cp.m_trees.Count, cp.m_frozen);
421 private void InstallCommands()
424 new Command(
"active",
CommandIntentions.COMMAND_HAZARDOUS, HandleTreeActive,
"Change activity state for the trees module");
425 treeActiveCommand.AddArgument(
"activeTF",
"The required activity state",
"Boolean");
428 new Command(
"freeze",
CommandIntentions.COMMAND_HAZARDOUS, HandleTreeFreeze,
"Freeze/Unfreeze activity for a defined copse");
429 treeFreezeCommand.AddArgument(
"copse",
"The required copse",
"String");
430 treeFreezeCommand.AddArgument(
"freezeTF",
"The required freeze state",
"Boolean");
433 new Command(
"load",
CommandIntentions.COMMAND_HAZARDOUS, HandleTreeLoad,
"Load a copse definition from an xml file");
434 treeLoadCommand.AddArgument(
"filename",
"The (xml) file you wish to load",
"String");
438 treePlantCommand.AddArgument(
"copse",
"The required copse",
"String");
442 treeRateCommand.AddArgument(
"updateRate",
"The required update rate (minimum 1000.0)",
"Double");
445 new Command(
"reload",
CommandIntentions.COMMAND_HAZARDOUS, HandleTreeReload,
"Reload copse definitions from the in-scene trees");
448 new Command(
"remove",
CommandIntentions.COMMAND_HAZARDOUS, HandleTreeRemove,
"Remove a copse definition and all its in-scene trees");
449 treeRemoveCommand.AddArgument(
"copse",
"The required copse",
"String");
451 Command treeStatisticsCommand =
452 new Command(
"statistics",
CommandIntentions.COMMAND_STATISTICAL, HandleTreeStatistics,
"Log statistics about the trees");
454 m_commander.RegisterCommand(
"active", treeActiveCommand);
455 m_commander.RegisterCommand(
"freeze", treeFreezeCommand);
456 m_commander.RegisterCommand(
"load", treeLoadCommand);
457 m_commander.RegisterCommand(
"plant", treePlantCommand);
458 m_commander.RegisterCommand(
"rate", treeRateCommand);
459 m_commander.RegisterCommand(
"reload", treeReloadCommand);
460 m_commander.RegisterCommand(
"remove", treeRemoveCommand);
461 m_commander.RegisterCommand(
"statistics", treeStatisticsCommand);
468 private void EventManager_OnPluginConsole(
string[] args)
470 if (args[0] ==
"tree")
472 if (args.Length == 1)
474 m_commander.ProcessConsoleCommand(
"help",
new string[0]);
478 string[] tmpArgs =
new string[args.Length - 2];
480 for (i = 2; i < args.Length; i++)
482 tmpArgs[i - 2] = args[i];
485 m_commander.ProcessConsoleCommand(args[1], tmpArgs);
490 #region IVegetationModule Members
493 UUID uuid, UUID groupID, Vector3 scale, Quaternion
rotation, Vector3 position, Tree treeType,
bool newTree)
496 treeShape.PathCurve = 16;
497 treeShape.PathEnd = 49900;
498 treeShape.PCode = newTree ? (byte)PCode.NewTree : (byte)PCode.Tree;
499 treeShape.Scale = scale;
500 treeShape.State = (byte)treeType;
502 return m_scene.AddNewPrim(uuid, groupID, position,
rotation, treeShape);
507 #region IEntityCreator Members
509 protected static readonly PCode[] creationCapabilities =
new PCode[] { PCode.NewTree, PCode.Tree };
510 public PCode[] CreationCapabilities {
get {
return creationCapabilities; } }
515 if (
Array.IndexOf(creationCapabilities, (PCode)shape.
PCode) < 0)
517 m_log.DebugFormat(
"[VEGETATION]: PCode {0} not handled by {1}", shape.PCode, Name);
524 rootPart.AddFlag(PrimFlags.Phantom);
526 m_scene.AddNewSceneObject(sceneObject,
true);
527 sceneObject.SetGroup(groupID, null);
536 #region Tree Utilities
541 XmlSerializer xs =
new XmlSerializer(typeof(
Copse));
543 using (XmlTextWriter writer =
new XmlTextWriter(fileName, Util.UTF8))
545 writer.Formatting = Formatting.Indented;
546 xs.Serialize(writer, obj);
549 catch (SystemException ex)
551 throw new ApplicationException(
"Unexpected failure in Tree serialization", ex);
559 XmlSerializer xs =
new XmlSerializer(typeof(
Copse));
561 using (FileStream fs =
new FileStream(fileName, FileMode.Open, FileAccess.Read))
562 return xs.Deserialize(fs);
564 catch (SystemException ex)
566 throw new ApplicationException(
"Unexpected failure in Tree de-serialization", ex);
570 private void ReloadCopse()
572 m_copse =
new List<Copse>();
579 SceneObjectGroup grp = (SceneObjectGroup)obj;
581 if (grp.
Name.Length > 5 && (grp.
Name.Substring(0, 5) ==
"ATPM:" || grp.Name.Substring(0, 5) ==
"FTPM:"))
587 Copse copse =
new Copse(grp.
Name);
589 foreach (Copse cp
in m_copse)
591 if (cp.m_name == copse.m_name)
594 cp.m_trees.Add(grp.UUID);
601 m_log.InfoFormat(
"[TREES]: Found copse {0}", grp.Name);
603 copse.m_trees.Add(grp.UUID);
608 m_log.InfoFormat(
"[TREES]: Ill formed copse definition {0} - ignoring", grp.Name);
616 private void activeizeTreeze(
bool activeYN)
620 CalculateTrees =
new Timer(m_update_ms);
621 CalculateTrees.Elapsed += CalculateTrees_Elapsed;
622 CalculateTrees.Start();
626 CalculateTrees.Stop();
630 private void growTrees()
632 foreach (Copse copse
in m_copse)
636 foreach (UUID tree
in copse.m_trees)
638 if (m_scene.Entities.ContainsKey(tree))
640 SceneObjectPart s_tree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
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)
644 s_tree.Scale += copse.m_rate;
645 s_tree.ParentGroup.HasGroupChanged =
true;
646 s_tree.ScheduleFullUpdate();
651 m_log.DebugFormat(
"[TREES]: Tree not in scene {0}", tree);
658 private void seedTrees()
660 foreach (Copse copse
in m_copse)
664 foreach (UUID tree
in copse.m_trees)
666 if (m_scene.Entities.ContainsKey(tree))
668 SceneObjectPart s_tree = ((SceneObjectGroup)m_scene.Entities[tree]).RootPart;
670 if (copse.m_trees.Count < copse.m_tree_quantity)
673 if (s_tree.
Scale.Z > copse.m_initial_scale.Z + (copse.m_maximum_scale.Z - copse.m_initial_scale.Z) / 4.0)
675 if (Util.RandomClass.NextDouble() > 0.75)
677 SpawnChild(copse, s_tree);
684 m_log.DebugFormat(
"[TREES]: Tree not in scene {0}", tree);
691 private void killTrees()
693 foreach (Copse copse
in m_copse)
695 if (!copse.m_frozen && copse.m_trees.Count >= copse.m_tree_quantity)
697 foreach (UUID tree
in copse.m_trees)
699 double killLikelyhood = 0.0;
701 if (m_scene.Entities.ContainsKey(tree))
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));
708 foreach (UUID picktree
in copse.m_trees)
710 if (picktree != tree)
712 SceneObjectPart pickedTree = ((SceneObjectGroup)m_scene.Entities[picktree]).RootPart;
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));
718 double pickedTreeDistance = Vector3.Distance(pickedTree.AbsolutePosition, selectedTree.AbsolutePosition);
720 killLikelyhood += (selectedTreeScale / (pickedTreeScale * pickedTreeDistance)) * 0.1;
724 if (Util.RandomClass.NextDouble() < killLikelyhood)
727 m_scene.DeleteSceneObject(selectedTree.ParentGroup,
false);
728 copse.m_trees.Remove(selectedTree.ParentGroup.UUID);
734 m_log.DebugFormat(
"[TREES]: Tree not in scene {0}", tree);
743 Vector3 position =
new Vector3();
745 double randX = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3);
746 double randY = ((Util.RandomClass.NextDouble() * 2.0) - 1.0) * (s_tree.Scale.X * 3);
748 position.X = s_tree.AbsolutePosition.X + (float)randX;
749 position.Y = s_tree.AbsolutePosition.Y + (float)randY;
751 if (position.X <= (m_scene.RegionInfo.RegionSizeX - 1) && position.X >= 0 &&
752 position.Y <= (m_scene.RegionInfo.RegionSizeY - 1) && position.Y >= 0 &&
753 Util.GetDistanceTo(position, copse.m_seed_point) <= copse.m_range)
755 UUID uuid = m_scene.RegionInfo.EstateSettings.EstateOwner;
757 CreateTree(uuid, copse, position);
761 private void CreateTree(UUID uuid, Copse copse, Vector3 position)
764 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)
767 SceneObjectGroup tree = AddTree(uuid,
UUID.Zero, copse.m_initial_scale, Quaternion.Identity, position, copse.m_tree_type,
false);
769 tree.Name = copse.ToString();
770 copse.m_trees.Add(tree.UUID);
771 tree.SendGroupFullUpdate();
775 private void CalculateTrees_Elapsed(
object sender, ElapsedEventArgs e)
static void SerializeObject(string fileName, Object obj)
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)
A scene object group is conceptually an object in the scene. The object is constituted of SceneObject...
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
SceneObjectGroup AddTree(UUID uuid, UUID groupID, Vector3 scale, Quaternion rotation, Vector3 position, Tree treeType, bool newTree)
Add a new tree to the scene. Used by other modules.
static object DeserializeObject(string fileName)
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
A single function call encapsulated in a class which enforces arguments when passing around as Object...
System.Timers.Timer Timer
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
Vector3 Scale
Change the scale of this part.
SceneObjectGroup CreateEntity(UUID ownerID, UUID groupID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape)
Create an entity
OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion rotation
A class to enable modules to register console and script commands, which enforces typing and valid in...
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
Version 2.02 - Still hacky
Interactive OpenSim region server
override string ToString()
Copse(string fileName, Boolean planted)
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
override string Name
The name of an object grouping is always the same as its root part