aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs')
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs3012
1 files changed, 3012 insertions, 0 deletions
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
new file mode 100644
index 0000000..57d9ce4
--- /dev/null
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
@@ -0,0 +1,3012 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Drawing;
31using System.IO;
32using System.Xml;
33using System.Xml.Serialization;
34using OpenMetaverse;
35using OpenMetaverse.Packets;
36using OpenSim.Framework;
37using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Physics.Manager;
39
40namespace OpenSim.Region.Framework.Scenes
41{
42 [Flags]
43 public enum scriptEvents
44 {
45 None = 0,
46 attach = 1,
47 collision = 16,
48 collision_end = 32,
49 collision_start = 64,
50 control = 128,
51 dataserver = 256,
52 email = 512,
53 http_response = 1024,
54 land_collision = 2048,
55 land_collision_end = 4096,
56 land_collision_start = 8192,
57 at_target = 16384,
58 listen = 32768,
59 money = 65536,
60 moving_end = 131072,
61 moving_start = 262144,
62 not_at_rot_target = 524288,
63 not_at_target = 1048576,
64 remote_data = 8388608,
65 run_time_permissions = 268435456,
66 state_entry = 1073741824,
67 state_exit = 2,
68 timer = 4,
69 touch = 8,
70 touch_end = 536870912,
71 touch_start = 2097152,
72 object_rez = 4194304
73 }
74
75 struct scriptPosTarget
76 {
77 public Vector3 targetPos;
78 public float tolerance;
79 }
80
81 public delegate void PrimCountTaintedDelegate();
82
83 /// <summary>
84 /// A scene object group is conceptually an object in the scene. The object is constituted of SceneObjectParts
85 /// (often known as prims), one of which is considered the root part.
86 /// </summary>
87 public partial class SceneObjectGroup : EntityBase
88 {
89 // private PrimCountTaintedDelegate handlerPrimCountTainted = null;
90
91 /// <summary>
92 /// Signal whether the non-inventory attributes of any prims in the group have changed
93 /// since the group's last persistent backup
94 /// </summary>
95 private bool m_hasGroupChanged = false;
96 private long timeFirstChanged;
97 private long timeLastChanged;
98
99 public bool HasGroupChanged
100 {
101 set
102 {
103 if (value)
104 {
105 timeLastChanged = DateTime.Now.Ticks;
106 if (!m_hasGroupChanged)
107 timeFirstChanged = DateTime.Now.Ticks;
108 }
109 m_hasGroupChanged = value;
110 }
111
112 get { return m_hasGroupChanged; }
113 }
114
115 private bool isTimeToPersist()
116 {
117 if (IsSelected || IsDeleted || IsAttachment)
118 return false;
119 if (!m_hasGroupChanged)
120 return false;
121 if (m_scene.ShuttingDown)
122 return true;
123 long currentTime = DateTime.Now.Ticks;
124 if (currentTime - timeLastChanged > m_scene.m_dontPersistBefore || currentTime - timeFirstChanged > m_scene.m_persistAfter)
125 return true;
126 return false;
127 }
128
129 /// <value>
130 /// Is this scene object acting as an attachment?
131 ///
132 /// We return false if the group has already been deleted.
133 ///
134 /// TODO: At the moment set must be done on the part itself. There may be a case for doing it here since I
135 /// presume either all or no parts in a linkset can be part of an attachment (in which
136 /// case the value would get proprogated down into all the descendent parts).
137 /// </value>
138 public bool IsAttachment
139 {
140 get
141 {
142 if (!IsDeleted)
143 return m_rootPart.IsAttachment;
144
145 return false;
146 }
147 }
148
149 public float scriptScore = 0f;
150
151 private Vector3 lastPhysGroupPos;
152 private Quaternion lastPhysGroupRot;
153
154 private bool m_isBackedUp = false;
155
156 /// <summary>
157 /// The constituent parts of this group
158 /// </summary>
159 protected Dictionary<UUID, SceneObjectPart> m_parts = new Dictionary<UUID, SceneObjectPart>();
160
161 protected ulong m_regionHandle;
162 protected SceneObjectPart m_rootPart;
163 // private Dictionary<UUID, scriptEvents> m_scriptEvents = new Dictionary<UUID, scriptEvents>();
164
165 private Dictionary<uint, scriptPosTarget> m_targets = new Dictionary<uint, scriptPosTarget>();
166
167 private bool m_scriptListens_atTarget = false;
168 private bool m_scriptListens_notAtTarget = false;
169
170 #region Properties
171
172 /// <summary>
173 /// The name of an object grouping is always the same as its root part
174 /// </summary>
175 public override string Name
176 {
177 get {
178 if (RootPart == null)
179 return "";
180 return RootPart.Name;
181 }
182 set { RootPart.Name = value; }
183 }
184
185 /// <summary>
186 /// Added because the Parcel code seems to use it
187 /// but not sure a object should have this
188 /// as what does it tell us? that some avatar has selected it (but not what Avatar/user)
189 /// think really there should be a list (or whatever) in each scenepresence
190 /// saying what prim(s) that user has selected.
191 /// </summary>
192 protected bool m_isSelected = false;
193
194 /// <summary>
195 /// Number of prims in this group
196 /// </summary>
197 public int PrimCount
198 {
199 get { return m_parts.Count; }
200 }
201
202 public Quaternion GroupRotation
203 {
204 get { return m_rootPart.RotationOffset; }
205 }
206
207 public UUID GroupID
208 {
209 get { return m_rootPart.GroupID; }
210 set { m_rootPart.GroupID = value; }
211 }
212
213 public Dictionary<UUID, SceneObjectPart> Children
214 {
215 get { return m_parts; }
216 set { m_parts = value; }
217 }
218
219 /// <value>
220 /// The root part of this scene object
221 /// </value>
222 public SceneObjectPart RootPart
223 {
224 get { return m_rootPart; }
225 }
226
227 public ulong RegionHandle
228 {
229 get { return m_regionHandle; }
230 set
231 {
232 m_regionHandle = value;
233 lock (m_parts)
234 {
235 foreach (SceneObjectPart part in m_parts.Values)
236 {
237 part.RegionHandle = m_regionHandle;
238 }
239 }
240 }
241 }
242
243 /// <summary>
244 /// The absolute position of this scene object in the scene
245 /// </summary>
246 public override Vector3 AbsolutePosition
247 {
248 get
249 {
250 if (m_rootPart == null)
251 {
252 throw new NullReferenceException(
253 string.Format("[SCENE OBJECT GROUP]: Object {0} has no root part.", m_uuid));
254 }
255
256 return m_rootPart.GroupPosition;
257 }
258 set
259 {
260 Vector3 val = value;
261
262 if ((val.X > 257f || val.X < -1f || val.Y > 257f || val.Y < -1f) && !IsAttachment)
263 {
264 m_scene.CrossPrimGroupIntoNewRegion(val, this, true);
265 }
266
267 lock (m_parts)
268 {
269 foreach (SceneObjectPart part in m_parts.Values)
270 {
271 part.GroupPosition = val;
272 }
273 }
274
275 //if (m_rootPart.PhysActor != null)
276 //{
277 //m_rootPart.PhysActor.Position =
278 //new PhysicsVector(m_rootPart.GroupPosition.X, m_rootPart.GroupPosition.Y,
279 //m_rootPart.GroupPosition.Z);
280 //m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
281 //}
282 }
283 }
284
285 public override uint LocalId
286 {
287 get
288 {
289 if (m_rootPart == null)
290 {
291 m_log.Error("[SCENE OBJECT GROUP]: Unable to find the rootpart for a LocalId Request!");
292 return 0;
293 }
294
295 return m_rootPart.LocalId;
296 }
297 set { m_rootPart.LocalId = value; }
298 }
299
300 public override UUID UUID
301 {
302 get {
303 if (m_rootPart == null)
304 {
305 m_log.Error("Got a null rootpart while requesting UUID. Called from: ", new Exception());
306 return UUID.Zero;
307 }
308 else return m_rootPart.UUID;
309 }
310 set { m_rootPart.UUID = value; }
311 }
312
313 public UUID OwnerID
314 {
315 get
316 {
317 if (m_rootPart == null)
318 return UUID.Zero;
319
320 return m_rootPart.OwnerID;
321 }
322 set { m_rootPart.OwnerID = value; }
323 }
324
325 public Color Color
326 {
327 get { return m_rootPart.Color; }
328 set { m_rootPart.Color = value; }
329 }
330
331 public string Text
332 {
333 get {
334 string returnstr = m_rootPart.Text;
335 if (returnstr.Length > 255)
336 {
337 returnstr = returnstr.Substring(0, 255);
338 }
339 return returnstr;
340 }
341 set { m_rootPart.Text = value; }
342 }
343
344 protected virtual bool InSceneBackup
345 {
346 get { return true; }
347 }
348
349 public bool IsSelected
350 {
351 get { return m_isSelected; }
352 set
353 {
354 m_isSelected = value;
355 // Tell physics engine that group is selected
356 if (m_rootPart != null && m_rootPart.PhysActor != null)
357 {
358 m_rootPart.PhysActor.Selected = value;
359 // Pass it on to the children.
360 foreach (SceneObjectPart child in Children.Values)
361 {
362 if (child.PhysActor != null)
363 {
364 child.PhysActor.Selected = value;
365 }
366 }
367 }
368 }
369 }
370
371 // The UUID for the Region this Object is in.
372 public UUID RegionUUID
373 {
374 get
375 {
376 if (m_scene != null)
377 {
378 return m_scene.RegionInfo.RegionID;
379 }
380 return UUID.Zero;
381 }
382 }
383
384 #endregion
385
386 #region Constructors
387
388 /// <summary>
389 /// Constructor
390 /// </summary>
391 public SceneObjectGroup()
392 {
393 }
394
395 /// <summary>
396 /// This constructor creates a SceneObjectGroup using a pre-existing SceneObjectPart.
397 /// The original SceneObjectPart will be used rather than a copy, preserving
398 /// its existing localID and UUID.
399 /// </summary>
400 public SceneObjectGroup(SceneObjectPart part)
401 {
402 SetRootPart(part);
403 }
404
405 public SceneObjectGroup(string xmlData, bool isOriginalXmlFormat)
406 : this(UUID.Zero, xmlData, isOriginalXmlFormat)
407 {
408 }
409
410 /// <summary>
411 /// Create an object using serialized data in OpenSim's original xml format.
412 /// </summary>
413 /// <param name="fromUserInventoryItemID">
414 /// If applicable, the user inventory item id from which this object was rezzed. If not applicable then this
415 /// should be UUID.Zero
416 /// </param>
417 /// <param name="xmlData"></param>
418 /// <param name="isOriginalXmlFormat">
419 /// This parameter only exists to separate the two different xml constructors. In the future, versions should
420 /// be specified within the xml itself.
421 /// </param>
422 public SceneObjectGroup(UUID fromUserInventoryItemID, string xmlData, bool isOriginalXmlFormat)
423 {
424 if (!isOriginalXmlFormat)
425 throw new Exception("This constructor must specify the xml is in OpenSim's original format");
426
427 //m_log.DebugFormat("[SOG]: Starting deserialization of SOG");
428 int time = System.Environment.TickCount;
429
430 // libomv.types changes UUID to Guid
431 xmlData = xmlData.Replace("<UUID>", "<Guid>");
432 xmlData = xmlData.Replace("</UUID>", "</Guid>");
433
434 // Handle Nested <UUID><UUID> property
435 xmlData = xmlData.Replace("<Guid><Guid>", "<UUID><Guid>");
436 xmlData = xmlData.Replace("</Guid></Guid>", "</Guid></UUID>");
437 StringReader sr = new StringReader(xmlData);
438 XmlTextReader reader = new XmlTextReader(sr);
439
440 try
441 {
442 reader.Read();
443 reader.ReadStartElement("SceneObjectGroup");
444 reader.ReadStartElement("RootPart");
445 SetRootPart(SceneObjectPart.FromXml(fromUserInventoryItemID, reader));
446
447 reader.ReadEndElement();
448
449 while (reader.Read())
450 {
451 switch (reader.NodeType)
452 {
453 case XmlNodeType.Element:
454 if (reader.Name == "Part")
455 {
456 reader.Read();
457 SceneObjectPart part = SceneObjectPart.FromXml(reader);
458
459 // We reset the link number in order to make sure that the persisted linkset order is
460 int linkNum = part.LinkNum;
461 AddPart(part);
462 part.LinkNum = linkNum;
463
464 part.TrimPermissions();
465 part.StoreUndoState();
466 }
467 break;
468
469 case XmlNodeType.EndElement:
470 break;
471 }
472 }
473 }
474 catch (XmlException e)
475 {
476 m_log.ErrorFormat("[SCENE]: Deserialization of xml failed with {0}. xml was {1}", e, xmlData);
477 }
478
479 reader.Close();
480 sr.Close();
481 m_log.DebugFormat("[SOG]: Finished deserialization of SOG {0}, {1}ms", Name, System.Environment.TickCount - time);
482 }
483
484 /// <summary>
485 /// Create an object using serialized data in OpenSim's xml2 format.
486 /// </summary>
487 public SceneObjectGroup(string xmlData)
488 {
489 SetFromXml(xmlData);
490 }
491
492 protected void SetFromXml(string xmlData)
493 {
494 //m_log.DebugFormat("[SOG]: Starting deserialization of SOG");
495 //int time = System.Environment.TickCount;
496
497 // libomv.types changes UUID to Guid
498 xmlData = xmlData.Replace("<UUID>", "<Guid>");
499 xmlData = xmlData.Replace("</UUID>", "</Guid>");
500
501 // Handle Nested <UUID><UUID> property
502 xmlData = xmlData.Replace("<Guid><Guid>", "<UUID><Guid>");
503 xmlData = xmlData.Replace("</Guid></Guid>", "</Guid></UUID>");
504
505 StringReader sr = new StringReader(xmlData);
506 XmlTextReader reader = new XmlTextReader(sr);
507 reader.Read();
508
509 reader.ReadStartElement("SceneObjectGroup");
510 SetRootPart(CreatePartFromXml(reader));
511
512 reader.Read();
513 bool more = true;
514
515 while (more)
516 {
517 switch (reader.NodeType)
518 {
519 case XmlNodeType.Element:
520 if (reader.Name == "SceneObjectPart")
521 {
522 SceneObjectPart part = CreatePartFromXml(reader);
523 AddPart(part);
524 part.StoreUndoState();
525 }
526 else
527 {
528 Console.WriteLine("found unexpected element: " + reader.Name);
529 reader.Read();
530 }
531 break;
532 case XmlNodeType.EndElement:
533 reader.Read();
534 break;
535 }
536 more = !reader.EOF;
537 }
538
539 reader.Close();
540 sr.Close();
541
542 //m_log.DebugFormat("[SOG]: Finished deserialization of SOG {0}, {1}ms", Name, System.Environment.TickCount - time);
543 }
544
545 protected virtual SceneObjectPart CreatePartFromXml(XmlTextReader reader)
546 {
547 SceneObjectPart part = SceneObjectPart.FromXml(reader);
548 return part;
549 }
550
551 /// <summary>
552 /// Constructor. This object is added to the scene later via AttachToScene()
553 /// </summary>
554 public SceneObjectGroup(UUID ownerID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape)
555 {
556 Vector3 rootOffset = new Vector3(0, 0, 0);
557 SetRootPart(new SceneObjectPart(ownerID, shape, pos, rot, rootOffset));
558 }
559
560 /// <summary>
561 /// Constructor.
562 /// </summary>
563 public SceneObjectGroup(UUID ownerID, Vector3 pos, PrimitiveBaseShape shape)
564 : this(ownerID, pos, Quaternion.Identity, shape)
565 {
566 }
567
568 public void SetFromAssetID(UUID AssetId)
569 {
570 lock (m_parts)
571 {
572 foreach (SceneObjectPart part in m_parts.Values)
573 {
574 part.FromAssetID = AssetId;
575 }
576 }
577 }
578
579 public UUID GetFromAssetID()
580 {
581 if (m_rootPart != null)
582 {
583 return m_rootPart.FromAssetID;
584 }
585 return UUID.Zero;
586 }
587
588 /// <summary>
589 /// Hooks this object up to the backup event so that it is persisted to the database when the update thread executes.
590 /// </summary>
591 public void AttachToBackup()
592 {
593 if (InSceneBackup)
594 {
595 //m_log.DebugFormat(
596 // "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID);
597
598 if (!m_isBackedUp)
599 m_scene.EventManager.OnBackup += ProcessBackup;
600
601 m_isBackedUp = true;
602 }
603 }
604
605 /// <summary>
606 /// Attach this object to a scene. It will also now appear to agents.
607 /// </summary>
608 /// <param name="scene"></param>
609 public void AttachToScene(Scene scene)
610 {
611 m_scene = scene;
612 RegionHandle = m_scene.RegionInfo.RegionHandle;
613
614 if (m_rootPart.Shape.PCode != 9 || m_rootPart.Shape.State == 0)
615 m_rootPart.ParentID = 0;
616 if (m_rootPart.LocalId==0)
617 m_rootPart.LocalId = m_scene.AllocateLocalId();
618
619 // No need to lock here since the object isn't yet in a scene
620 foreach (SceneObjectPart part in m_parts.Values)
621 {
622 if (Object.ReferenceEquals(part, m_rootPart))
623 continue;
624 if (part.LocalId==0)
625 part.LocalId = m_scene.AllocateLocalId();
626 part.ParentID = m_rootPart.LocalId;
627 //m_log.DebugFormat("[SCENE]: Given local id {0} to part {1}, linknum {2}, parent {3} {4}", part.LocalId, part.UUID, part.LinkNum, part.ParentID, part.ParentUUID);
628 }
629
630 ApplyPhysics(m_scene.m_physicalPrim);
631
632 ScheduleGroupForFullUpdate();
633 }
634
635 public Vector3 GroupScale()
636 {
637 Vector3 minScale = new Vector3(Constants.RegionSize,Constants.RegionSize,Constants.RegionSize);
638 Vector3 maxScale = new Vector3(0f,0f,0f);
639 Vector3 finalScale = new Vector3(0.5f, 0.5f, 0.5f);
640
641 lock (m_parts)
642 {
643 foreach (SceneObjectPart part in m_parts.Values)
644 {
645 Vector3 partscale = part.Scale;
646 Vector3 partoffset = part.OffsetPosition;
647
648 minScale.X = (partscale.X + partoffset.X < minScale.X) ? partscale.X + partoffset.X : minScale.X;
649 minScale.Y = (partscale.Y + partoffset.Y < minScale.Y) ? partscale.X + partoffset.Y : minScale.Y;
650 minScale.Z = (partscale.Z + partoffset.Z < minScale.Z) ? partscale.X + partoffset.Z : minScale.Z;
651
652 maxScale.X = (partscale.X + partoffset.X > maxScale.X) ? partscale.X + partoffset.X : maxScale.X;
653 maxScale.Y = (partscale.Y + partoffset.Y > maxScale.Y) ? partscale.Y + partoffset.Y : maxScale.Y;
654 maxScale.Z = (partscale.Z + partoffset.Z > maxScale.Z) ? partscale.Z + partoffset.Z : maxScale.Z;
655 }
656 }
657 finalScale.X = (minScale.X > maxScale.X) ? minScale.X : maxScale.X;
658 finalScale.Y = (minScale.Y > maxScale.Y) ? minScale.Y : maxScale.Y;
659 finalScale.Z = (minScale.Z > maxScale.Z) ? minScale.Z : maxScale.Z;
660 return finalScale;
661
662 }
663 public EntityIntersection TestIntersection(Ray hRay, bool frontFacesOnly, bool faceCenters)
664 {
665 // We got a request from the inner_scene to raytrace along the Ray hRay
666 // We're going to check all of the prim in this group for intersection with the ray
667 // If we get a result, we're going to find the closest result to the origin of the ray
668 // and send back the intersection information back to the innerscene.
669
670 EntityIntersection returnresult = new EntityIntersection();
671
672 lock (m_parts)
673 {
674 foreach (SceneObjectPart part in m_parts.Values)
675 {
676 // Temporary commented to stop compiler warning
677 //Vector3 partPosition =
678 // new Vector3(part.AbsolutePosition.X, part.AbsolutePosition.Y, part.AbsolutePosition.Z);
679 Quaternion parentrotation = GroupRotation;
680
681 // Telling the prim to raytrace.
682 //EntityIntersection inter = part.TestIntersection(hRay, parentrotation);
683
684 EntityIntersection inter = part.TestIntersectionOBB(hRay, parentrotation,frontFacesOnly, faceCenters);
685
686 // This may need to be updated to the maximum draw distance possible..
687 // We might (and probably will) be checking for prim creation from other sims
688 // when the camera crosses the border.
689 float idist = Constants.RegionSize;
690
691
692 if (inter.HitTF)
693 {
694 // We need to find the closest prim to return to the testcaller along the ray
695 if (inter.distance < idist)
696 {
697 returnresult.HitTF = true;
698 returnresult.ipoint = inter.ipoint;
699 returnresult.obj = part;
700 returnresult.normal = inter.normal;
701 returnresult.distance = inter.distance;
702 }
703 }
704 }
705 }
706 return returnresult;
707 }
708
709 #endregion
710
711
712 public string ToXmlString()
713 {
714 using (StringWriter sw = new StringWriter())
715 {
716 using (XmlTextWriter writer = new XmlTextWriter(sw))
717 {
718 ToXml(writer);
719 }
720
721 return sw.ToString();
722 }
723 }
724
725 public void ToXml(XmlTextWriter writer)
726 {
727 m_log.DebugFormat("[SOG]: Starting serialization of {0}", Name);
728 int time = System.Environment.TickCount;
729
730 writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty);
731 writer.WriteStartElement(String.Empty, "RootPart", String.Empty);
732 m_rootPart.ToXml(writer);
733 writer.WriteEndElement();
734 writer.WriteStartElement(String.Empty, "OtherParts", String.Empty);
735
736 lock (m_parts)
737 {
738 foreach (SceneObjectPart part in m_parts.Values)
739 {
740 if (part.UUID != m_rootPart.UUID)
741 {
742 writer.WriteStartElement(String.Empty, "Part", String.Empty);
743 part.ToXml(writer);
744 writer.WriteEndElement();
745 }
746 }
747 }
748
749 writer.WriteEndElement();
750 writer.WriteEndElement();
751
752 m_log.DebugFormat("[SOG]: Finished serialization of SOG {0}, {1}ms", Name, System.Environment.TickCount - time);
753
754 }
755
756 public string ToXmlString2()
757 {
758 using (StringWriter sw = new StringWriter())
759 {
760 using (XmlTextWriter writer = new XmlTextWriter(sw))
761 {
762 ToXml2(writer);
763 }
764
765 return sw.ToString();
766 }
767 }
768
769 public void ToXml2(XmlTextWriter writer)
770 {
771 m_log.DebugFormat("[SOG]: Starting serialization of SOG {0} to XML2", Name);
772 int time = System.Environment.TickCount;
773
774 writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty);
775 m_rootPart.ToXml(writer);
776 writer.WriteStartElement(String.Empty, "OtherParts", String.Empty);
777
778 lock (m_parts)
779 {
780 foreach (SceneObjectPart part in m_parts.Values)
781 {
782 if (part.UUID != m_rootPart.UUID)
783 {
784 part.ToXml(writer);
785 }
786 }
787 }
788
789 writer.WriteEndElement();
790 writer.WriteEndElement();
791 m_log.DebugFormat("[SOG]: Finished serialization of SOG {0} to XML2, {1}ms", Name, System.Environment.TickCount - time);
792 }
793
794 /// <summary>
795 /// Attach this scene object to the given avatar.
796 /// </summary>
797 /// <param name="agentID"></param>
798 /// <param name="attachmentpoint"></param>
799 /// <param name="AttachOffset"></param>
800 public void AttachToAgent(UUID agentID, uint attachmentpoint, Vector3 AttachOffset, bool silent)
801 {
802 ScenePresence avatar = m_scene.GetScenePresence(agentID);
803 if (avatar != null)
804 {
805 // don't attach attachments to child agents
806 if (avatar.IsChildAgent) return;
807
808 DetachFromBackup();
809
810 // Remove from database and parcel prim count
811 //
812 m_scene.DeleteFromStorage(UUID);
813 m_scene.EventManager.TriggerParcelPrimCountTainted();
814
815 m_rootPart.AttachedAvatar = agentID;
816
817 if (m_rootPart.PhysActor != null)
818 {
819 m_scene.PhysicsScene.RemovePrim(m_rootPart.PhysActor);
820 m_rootPart.PhysActor = null;
821 }
822
823 AbsolutePosition = AttachOffset;
824 m_rootPart.AttachedPos = AttachOffset;
825 m_rootPart.IsAttachment = true;
826
827 m_rootPart.SetParentLocalId(avatar.LocalId);
828 SetAttachmentPoint(Convert.ToByte(attachmentpoint));
829
830 avatar.AddAttachment(this);
831
832 if (!silent)
833 {
834 // Killing it here will cause the client to deselect it
835 // It then reappears on the avatar, deselected
836 // through the full update below
837 //
838 if (IsSelected)
839 {
840 m_scene.SendKillObject(m_rootPart.LocalId);
841 }
842
843 IsSelected = false; // fudge....
844 ScheduleGroupForFullUpdate();
845 }
846 }
847 }
848
849 public byte GetAttachmentPoint()
850 {
851 if (m_rootPart != null)
852 {
853 return m_rootPart.Shape.State;
854 }
855 return (byte)0;
856 }
857
858 public void ClearPartAttachmentData()
859 {
860 SetAttachmentPoint((Byte)0);
861 }
862
863 public void DetachToGround()
864 {
865 ScenePresence avatar = m_scene.GetScenePresence(m_rootPart.AttachedAvatar);
866 if (avatar == null)
867 return;
868
869 avatar.RemoveAttachment(this);
870
871 Vector3 detachedpos = new Vector3(127f,127f,127f);
872 if (avatar == null)
873 return;
874
875 detachedpos = avatar.AbsolutePosition;
876
877 AbsolutePosition = detachedpos;
878 m_rootPart.AttachedAvatar = UUID.Zero;
879 m_rootPart.SetParentLocalId(0);
880 SetAttachmentPoint((byte)0);
881 m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive, m_scene.m_physicalPrim);
882 HasGroupChanged = true;
883 RootPart.Rezzed = DateTime.Now;
884 RootPart.RemFlag(PrimFlags.TemporaryOnRez);
885 AttachToBackup();
886 m_scene.EventManager.TriggerParcelPrimCountTainted();
887 m_rootPart.ScheduleFullUpdate();
888 m_rootPart.ClearUndoState();
889 }
890
891 public void DetachToInventoryPrep()
892 {
893 ScenePresence avatar = m_scene.GetScenePresence(m_rootPart.AttachedAvatar);
894 //Vector3 detachedpos = new Vector3(127f, 127f, 127f);
895 if (avatar != null)
896 {
897 //detachedpos = avatar.AbsolutePosition;
898 avatar.RemoveAttachment(this);
899 }
900
901 m_rootPart.AttachedAvatar = UUID.Zero;
902 m_rootPart.SetParentLocalId(0);
903 //m_rootPart.SetAttachmentPoint((byte)0);
904 m_rootPart.IsAttachment = false;
905 AbsolutePosition = m_rootPart.AttachedPos;
906 //m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_scene.m_physicalPrim);
907 //AttachToBackup();
908 //m_rootPart.ScheduleFullUpdate();
909
910 }
911 /// <summary>
912 ///
913 /// </summary>
914 /// <param name="part"></param>
915 private void SetPartAsNonRoot(SceneObjectPart part)
916 {
917 part.ParentID = m_rootPart.LocalId;
918 part.ClearUndoState();
919 }
920
921 public override void UpdateMovement()
922 {
923 lock (m_parts)
924 {
925 foreach (SceneObjectPart part in m_parts.Values)
926 {
927 part.UpdateMovement();
928 }
929 }
930 }
931
932 public float GetTimeDilation()
933 {
934 return m_scene.TimeDilation;
935 }
936
937 /// <summary>
938 /// Added as a way for the storage provider to reset the scene,
939 /// most likely a better way to do this sort of thing but for now...
940 /// </summary>
941 /// <param name="scene"></param>
942 public void SetScene(Scene scene)
943 {
944 m_scene = scene;
945 }
946
947 /// <summary>
948 /// Set a part to act as the root part for this scene object
949 /// </summary>
950 /// <param name="part"></param>
951 public void SetRootPart(SceneObjectPart part)
952 {
953 part.SetParent(this);
954 m_rootPart = part;
955 if (!IsAttachment)
956 part.ParentID = 0;
957 part.LinkNum = 0;
958
959 // No locking required since the SOG should not be in the scene yet - one can't change root parts after
960 // the scene object has been attached to the scene
961 m_parts.Add(m_rootPart.UUID, m_rootPart);
962 }
963
964 /// <summary>
965 /// Add a new part to this scene object. The part must already be correctly configured.
966 /// </summary>
967 /// <param name="part"></param>
968 public void AddPart(SceneObjectPart part)
969 {
970 lock (m_parts)
971 {
972 part.SetParent(this);
973 m_parts.Add(part.UUID, part);
974
975 part.LinkNum = m_parts.Count;
976
977 if (part.LinkNum == 2 && RootPart != null)
978 RootPart.LinkNum = 1;
979 }
980 }
981
982 /// <summary>
983 /// Make sure that every non root part has the proper parent root part local id
984 /// </summary>
985 private void UpdateParentIDs()
986 {
987 lock (m_parts)
988 {
989 foreach (SceneObjectPart part in m_parts.Values)
990 {
991 if (part.UUID != m_rootPart.UUID)
992 {
993 part.ParentID = m_rootPart.LocalId;
994 }
995 }
996 }
997 }
998
999 public void RegenerateFullIDs()
1000 {
1001 lock (m_parts)
1002 {
1003 foreach (SceneObjectPart part in m_parts.Values)
1004 {
1005 part.UUID = UUID.Random();
1006
1007 }
1008 }
1009 }
1010 // helper provided for parts.
1011 public int GetSceneMaxUndo()
1012 {
1013 if (m_scene != null)
1014 return m_scene.MaxUndoCount;
1015 return 5;
1016 }
1017
1018 // justincc: I don't believe this hack is needed any longer, especially since the physics
1019 // parts of set AbsolutePosition were already commented out. By changing HasGroupChanged to false
1020 // this method was preventing proper reload of scene objects.
1021 // dahlia: I had to uncomment it, without it meshing was failing on some prims and objects
1022 // at region startup
1023 public void ResetChildPrimPhysicsPositions()
1024 {
1025 AbsolutePosition = AbsolutePosition; // could someone in the know please explain how this works?
1026 }
1027
1028 public UUID GetPartsFullID(uint localID)
1029 {
1030 SceneObjectPart part = GetChildPart(localID);
1031 if (part != null)
1032 {
1033 return part.UUID;
1034 }
1035 return UUID.Zero;
1036 }
1037
1038 public void ObjectGrabHandler(uint localId, Vector3 offsetPos, IClientAPI remoteClient)
1039 {
1040 if (m_rootPart.LocalId == localId)
1041 {
1042 OnGrabGroup(offsetPos, remoteClient);
1043 }
1044 else
1045 {
1046 SceneObjectPart part = GetChildPart(localId);
1047 OnGrabPart(part, offsetPos, remoteClient);
1048
1049 }
1050 }
1051
1052 public virtual void OnGrabPart(SceneObjectPart part, Vector3 offsetPos, IClientAPI remoteClient)
1053 {
1054 part.StoreUndoState();
1055 part.OnGrab(offsetPos, remoteClient);
1056 }
1057
1058 public virtual void OnGrabGroup(Vector3 offsetPos, IClientAPI remoteClient)
1059 {
1060 m_scene.EventManager.TriggerGroupGrab(UUID, offsetPos, remoteClient.AgentId);
1061 }
1062
1063 /// <summary>
1064 /// Delete this group from its scene and tell all the scene presences about that deletion.
1065 /// </summary>
1066 /// <param name="silent">Broadcast deletions to all clients.</param>
1067 public void DeleteGroup(bool silent)
1068 {
1069 // We need to keep track of this state in case this group is still queued for backup.
1070 m_isDeleted = true;
1071
1072 DetachFromBackup();
1073
1074 lock (m_parts)
1075 {
1076 foreach (SceneObjectPart part in m_parts.Values)
1077 {
1078// part.Inventory.RemoveScriptInstances();
1079
1080 List<ScenePresence> avatars = Scene.GetScenePresences();
1081 for (int i = 0; i < avatars.Count; i++)
1082 {
1083 if (avatars[i].ParentID == LocalId)
1084 {
1085 avatars[i].StandUp();
1086 }
1087
1088 if (!silent)
1089 {
1090 if (m_rootPart != null && part == m_rootPart)
1091 avatars[i].ControllingClient.SendKillObject(m_regionHandle, part.LocalId);
1092 }
1093 }
1094 }
1095 }
1096 }
1097
1098 public void AddScriptLPS(int count)
1099 {
1100 if (scriptScore + count >= float.MaxValue - count)
1101 scriptScore = 0;
1102
1103 scriptScore += (float)count;
1104 SceneGraph d = m_scene.m_sceneGraph;
1105 d.AddToScriptLPS(count);
1106 }
1107
1108 public void AddActiveScriptCount(int count)
1109 {
1110 SceneGraph d = m_scene.m_sceneGraph;
1111 d.AddActiveScripts(count);
1112 }
1113
1114 public void aggregateScriptEvents()
1115 {
1116 uint objectflagupdate=(uint)RootPart.GetEffectiveObjectFlags();
1117
1118 scriptEvents aggregateScriptEvents=0;
1119
1120 lock (m_parts)
1121 {
1122 foreach (SceneObjectPart part in m_parts.Values)
1123 {
1124 if (part == null)
1125 continue;
1126 if (part != RootPart)
1127 part.ObjectFlags = objectflagupdate;
1128 aggregateScriptEvents |= part.AggregateScriptEvents;
1129 }
1130 }
1131
1132 if ((aggregateScriptEvents & scriptEvents.at_target) != 0)
1133 {
1134 m_scriptListens_atTarget = true;
1135 }
1136 else
1137 {
1138 m_scriptListens_atTarget = false;
1139 }
1140
1141 if ((aggregateScriptEvents & scriptEvents.not_at_target) != 0)
1142 {
1143 m_scriptListens_notAtTarget = true;
1144 }
1145 else
1146 {
1147 m_scriptListens_notAtTarget = false;
1148 }
1149
1150 if (m_scriptListens_atTarget || m_scriptListens_notAtTarget)
1151 {
1152 }
1153 else
1154 {
1155 lock (m_targets)
1156 m_targets.Clear();
1157 }
1158
1159 ScheduleGroupForFullUpdate();
1160 }
1161
1162 public override void SetText(string text, Vector3 color, double alpha)
1163 {
1164 Color = Color.FromArgb(0xff - (int) (alpha * 0xff),
1165 (int) (color.X * 0xff),
1166 (int) (color.Y * 0xff),
1167 (int) (color.Z * 0xff));
1168 Text = text;
1169
1170 HasGroupChanged = true;
1171 m_rootPart.ScheduleFullUpdate();
1172 }
1173
1174 /// <summary>
1175 /// Apply physics to this group
1176 /// </summary>
1177 /// <param name="m_physicalPrim"></param>
1178 public void ApplyPhysics(bool m_physicalPrim)
1179 {
1180 lock (m_parts)
1181 {
1182 if (m_parts.Count > 1)
1183 {
1184 m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive, m_physicalPrim);
1185 foreach (SceneObjectPart part in m_parts.Values)
1186 {
1187 if (part.LocalId != m_rootPart.LocalId)
1188 {
1189 part.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), part.VolumeDetectActive, m_physicalPrim);
1190 }
1191 }
1192
1193 // Hack to get the physics scene geometries in the right spot
1194 ResetChildPrimPhysicsPositions();
1195 }
1196 else
1197 {
1198 m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive, m_physicalPrim);
1199 }
1200 }
1201 }
1202
1203 public void SetOwnerId(UUID userId)
1204 {
1205 ForEachPart(delegate(SceneObjectPart part) { part.OwnerID = userId; });
1206 }
1207
1208 public void ForEachPart(Action<SceneObjectPart> whatToDo)
1209 {
1210 lock (m_parts)
1211 {
1212 foreach (SceneObjectPart part in m_parts.Values)
1213 {
1214 whatToDo(part);
1215 }
1216 }
1217 }
1218
1219 #region Events
1220
1221 /// <summary>
1222 /// Processes backup.
1223 /// </summary>
1224 /// <param name="datastore"></param>
1225 public void ProcessBackup(IRegionDataStore datastore, bool forcedBackup)
1226 {
1227 if (!m_isBackedUp)
1228 return;
1229
1230 // Since this is the top of the section of call stack for backing up a particular scene object, don't let
1231 // any exception propogate upwards.
1232
1233 if (IsDeleted || UUID == UUID.Zero)
1234 return;
1235
1236 try
1237 {
1238 if (!m_scene.ShuttingDown) // if shutting down then there will be nothing to handle the return so leave till next restart
1239 {
1240 ILandObject parcel = m_scene.LandChannel.GetLandObject(
1241 m_rootPart.GroupPosition.X, m_rootPart.GroupPosition.Y);
1242
1243 if (parcel != null && parcel.landData != null &&
1244 parcel.landData.OtherCleanTime != 0)
1245 {
1246 if (parcel.landData.OwnerID != OwnerID &&
1247 (parcel.landData.GroupID != GroupID ||
1248 parcel.landData.GroupID == UUID.Zero))
1249 {
1250 if ((DateTime.Now - RootPart.Rezzed).TotalMinutes >
1251 parcel.landData.OtherCleanTime)
1252 {
1253 DetachFromBackup();
1254 m_log.InfoFormat("[SCENE]: Returning object {0} due to parcel auto return", RootPart.UUID.ToString());
1255 m_scene.AddReturn(OwnerID, Name, AbsolutePosition, "parcel auto return");
1256 m_scene.DeRezObject(null, RootPart.LocalId,
1257 RootPart.GroupID, DeRezAction.Return, UUID.Zero);
1258
1259 return;
1260 }
1261 }
1262 }
1263 }
1264
1265 if (HasGroupChanged)
1266 {
1267 // don't backup while it's selected or you're asking for changes mid stream.
1268 if ((isTimeToPersist()) || (forcedBackup))
1269 {
1270 m_log.DebugFormat(
1271 "[SCENE]: Storing {0}, {1} in {2}",
1272 Name, UUID, m_scene.RegionInfo.RegionName);
1273
1274 SceneObjectGroup backup_group = Copy(OwnerID, GroupID, false);
1275 backup_group.RootPart.Velocity = RootPart.Velocity;
1276 backup_group.RootPart.Acceleration = RootPart.Acceleration;
1277 backup_group.RootPart.AngularVelocity = RootPart.AngularVelocity;
1278 backup_group.RootPart.ParticleSystem = RootPart.ParticleSystem;
1279 HasGroupChanged = false;
1280
1281 datastore.StoreObject(backup_group, m_scene.RegionInfo.RegionID);
1282
1283 backup_group.ForEachPart(delegate(SceneObjectPart part)
1284 {
1285 part.Inventory.ProcessInventoryBackup(datastore);
1286 });
1287
1288 backup_group = null;
1289 }
1290 // else
1291 // {
1292 // m_log.DebugFormat(
1293 // "[SCENE]: Did not update persistence of object {0} {1}, selected = {2}",
1294 // Name, UUID, IsSelected);
1295 // }
1296 }
1297 }
1298 catch (Exception e)
1299 {
1300 m_log.ErrorFormat(
1301 "[SCENE]: Storing of {0}, {1} in {2} failed with exception {3}\n\t{4}",
1302 Name, UUID, m_scene.RegionInfo.RegionName, e, e.StackTrace);
1303 }
1304 }
1305
1306 #endregion
1307
1308 #region Client Updating
1309
1310 public void SendFullUpdateToClient(IClientAPI remoteClient)
1311 {
1312 SendPartFullUpdate(remoteClient, RootPart, m_scene.Permissions.GenerateClientFlags(remoteClient.AgentId, RootPart.UUID));
1313
1314 lock (m_parts)
1315 {
1316 foreach (SceneObjectPart part in m_parts.Values)
1317 {
1318 if (part != RootPart)
1319 SendPartFullUpdate(remoteClient, part, m_scene.Permissions.GenerateClientFlags(remoteClient.AgentId, part.UUID));
1320 }
1321 }
1322 }
1323
1324 /// <summary>
1325 /// Send a full update to the client for the given part
1326 /// </summary>
1327 /// <param name="remoteClient"></param>
1328 /// <param name="part"></param>
1329 internal void SendPartFullUpdate(IClientAPI remoteClient, SceneObjectPart part, uint clientFlags)
1330 {
1331 if (m_rootPart != null && m_rootPart.UUID == part.UUID)
1332 {
1333 if (IsAttachment)
1334 {
1335 part.SendFullUpdateToClient(remoteClient, m_rootPart.AttachedPos, clientFlags);
1336 }
1337 else
1338 {
1339 part.SendFullUpdateToClient(remoteClient, AbsolutePosition, clientFlags);
1340 }
1341 }
1342 else
1343 {
1344 part.SendFullUpdateToClient(remoteClient, clientFlags);
1345 }
1346 }
1347
1348 #endregion
1349
1350 #region Copying
1351
1352 /// <summary>
1353 /// Duplicates this object, including operations such as physics set up and attaching to the backup event.
1354 /// </summary>
1355 /// <returns></returns>
1356 public SceneObjectGroup Copy(UUID cAgentID, UUID cGroupID, bool userExposed)
1357 {
1358 SceneObjectGroup dupe = (SceneObjectGroup)MemberwiseClone();
1359 dupe.m_isBackedUp = false;
1360 dupe.m_parts = new Dictionary<UUID, SceneObjectPart>();
1361 dupe.AbsolutePosition = new Vector3(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z);
1362
1363 dupe.CopyRootPart(m_rootPart, OwnerID, GroupID, userExposed);
1364 dupe.m_rootPart.LinkNum = m_rootPart.LinkNum;
1365
1366 if (userExposed)
1367 dupe.m_rootPart.TrimPermissions();
1368
1369 /// may need to create a new Physics actor.
1370 if (dupe.RootPart.PhysActor != null && userExposed)
1371 {
1372 PrimitiveBaseShape pbs = dupe.RootPart.Shape;
1373
1374 dupe.RootPart.PhysActor = m_scene.PhysicsScene.AddPrimShape(
1375 dupe.RootPart.Name,
1376 pbs,
1377 new PhysicsVector(dupe.RootPart.AbsolutePosition.X, dupe.RootPart.AbsolutePosition.Y, dupe.RootPart.AbsolutePosition.Z),
1378 new PhysicsVector(dupe.RootPart.Scale.X, dupe.RootPart.Scale.Y, dupe.RootPart.Scale.Z),
1379 dupe.RootPart.RotationOffset,
1380 dupe.RootPart.PhysActor.IsPhysical);
1381
1382 dupe.RootPart.PhysActor.LocalID = dupe.RootPart.LocalId;
1383 dupe.RootPart.DoPhysicsPropertyUpdate(dupe.RootPart.PhysActor.IsPhysical, true);
1384 }
1385
1386 // Now we've made a copy that replaces this one, we need to
1387 // switch the owner to the person who did the copying
1388 // Second Life copies an object and duplicates the first one in it's place
1389 // So, we have to make a copy of this one, set it in it's place then set the owner on this one
1390 if (userExposed)
1391 {
1392 SetRootPartOwner(m_rootPart, cAgentID, cGroupID);
1393 m_rootPart.ScheduleFullUpdate();
1394 }
1395
1396 List<SceneObjectPart> partList;
1397
1398 lock (m_parts)
1399 {
1400 partList = new List<SceneObjectPart>(m_parts.Values);
1401 }
1402
1403 partList.Sort(delegate(SceneObjectPart p1, SceneObjectPart p2)
1404 {
1405 return p1.LinkNum.CompareTo(p2.LinkNum);
1406 }
1407 );
1408
1409 foreach (SceneObjectPart part in partList)
1410 {
1411 if (part.UUID != m_rootPart.UUID)
1412 {
1413 SceneObjectPart newPart = dupe.CopyPart(part, OwnerID, GroupID, userExposed);
1414
1415 newPart.LinkNum = part.LinkNum;
1416
1417 if (userExposed)
1418 {
1419 SetPartOwner(newPart, cAgentID, cGroupID);
1420 newPart.ScheduleFullUpdate();
1421 }
1422 }
1423 }
1424
1425 if (userExposed)
1426 {
1427 dupe.UpdateParentIDs();
1428 dupe.HasGroupChanged = true;
1429 dupe.AttachToBackup();
1430
1431 ScheduleGroupForFullUpdate();
1432 }
1433
1434 return dupe;
1435 }
1436
1437 /// <summary>
1438 ///
1439 /// </summary>
1440 /// <param name="part"></param>
1441 /// <param name="cAgentID"></param>
1442 /// <param name="cGroupID"></param>
1443 public void CopyRootPart(SceneObjectPart part, UUID cAgentID, UUID cGroupID, bool userExposed)
1444 {
1445 SetRootPart(part.Copy(m_scene.AllocateLocalId(), OwnerID, GroupID, m_parts.Count, userExposed));
1446 }
1447
1448 public void ScriptSetPhysicsStatus(bool UsePhysics)
1449 {
1450 bool IsTemporary = ((RootPart.Flags & PrimFlags.TemporaryOnRez) != 0);
1451 bool IsPhantom = ((RootPart.Flags & PrimFlags.Phantom) != 0);
1452 bool IsVolumeDetect = RootPart.VolumeDetectActive;
1453 UpdatePrimFlags(RootPart.LocalId, UsePhysics, IsTemporary, IsPhantom, IsVolumeDetect);
1454 }
1455
1456 public void ScriptSetTemporaryStatus(bool TemporaryStatus)
1457 {
1458 bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0);
1459 bool IsPhantom = ((RootPart.Flags & PrimFlags.Phantom) != 0);
1460 bool IsVolumeDetect = RootPart.VolumeDetectActive;
1461 UpdatePrimFlags(RootPart.LocalId, UsePhysics, TemporaryStatus, IsPhantom, IsVolumeDetect);
1462 }
1463
1464 public void ScriptSetPhantomStatus(bool PhantomStatus)
1465 {
1466 bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0);
1467 bool IsTemporary = ((RootPart.Flags & PrimFlags.TemporaryOnRez) != 0);
1468 bool IsVolumeDetect = RootPart.VolumeDetectActive;
1469 UpdatePrimFlags(RootPart.LocalId, UsePhysics, IsTemporary, PhantomStatus, IsVolumeDetect);
1470 }
1471
1472 public void ScriptSetVolumeDetect(bool VDStatus)
1473 {
1474 bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0);
1475 bool IsTemporary = ((RootPart.Flags & PrimFlags.TemporaryOnRez) != 0);
1476 bool IsPhantom = ((RootPart.Flags & PrimFlags.Phantom) != 0);
1477 UpdatePrimFlags(RootPart.LocalId, UsePhysics, IsTemporary, IsPhantom, VDStatus);
1478
1479 /*
1480 ScriptSetPhantomStatus(false); // What ever it was before, now it's not phantom anymore
1481
1482 if (PhysActor != null) // Should always be the case now
1483 {
1484 PhysActor.SetVolumeDetect(param);
1485 }
1486 if (param != 0)
1487 AddFlag(PrimFlags.Phantom);
1488
1489 ScheduleFullUpdate();
1490 */
1491 }
1492
1493 public void applyImpulse(PhysicsVector impulse)
1494 {
1495 // We check if rootpart is null here because scripts don't delete if you delete the host.
1496 // This means that unfortunately, we can pass a null physics actor to Simulate!
1497 // Make sure we don't do that!
1498 SceneObjectPart rootpart = m_rootPart;
1499 if (rootpart != null)
1500 {
1501 if (rootpart.PhysActor != null)
1502 {
1503 if (IsAttachment)
1504 {
1505 ScenePresence avatar = m_scene.GetScenePresence(rootpart.AttachedAvatar);
1506 if (avatar != null)
1507 {
1508 avatar.PushForce(impulse);
1509 }
1510 }
1511 else
1512 {
1513 rootpart.PhysActor.AddForce(impulse,true);
1514 m_scene.PhysicsScene.AddPhysicsActorTaint(rootpart.PhysActor);
1515 }
1516 }
1517 }
1518 }
1519
1520 public void applyAngularImpulse(PhysicsVector impulse)
1521 {
1522 // We check if rootpart is null here because scripts don't delete if you delete the host.
1523 // This means that unfortunately, we can pass a null physics actor to Simulate!
1524 // Make sure we don't do that!
1525 SceneObjectPart rootpart = m_rootPart;
1526 if (rootpart != null)
1527 {
1528 if (rootpart.PhysActor != null)
1529 {
1530 if (!IsAttachment)
1531 {
1532 rootpart.PhysActor.AddAngularForce(impulse, true);
1533 m_scene.PhysicsScene.AddPhysicsActorTaint(rootpart.PhysActor);
1534 }
1535 }
1536 }
1537 }
1538
1539 public void setAngularImpulse(PhysicsVector impulse)
1540 {
1541 // We check if rootpart is null here because scripts don't delete if you delete the host.
1542 // This means that unfortunately, we can pass a null physics actor to Simulate!
1543 // Make sure we don't do that!
1544 SceneObjectPart rootpart = m_rootPart;
1545 if (rootpart != null)
1546 {
1547 if (rootpart.PhysActor != null)
1548 {
1549 if (!IsAttachment)
1550 {
1551 rootpart.PhysActor.Torque = impulse;
1552 m_scene.PhysicsScene.AddPhysicsActorTaint(rootpart.PhysActor);
1553 }
1554 }
1555 }
1556 }
1557
1558 public Vector3 GetTorque()
1559 {
1560 // We check if rootpart is null here because scripts don't delete if you delete the host.
1561 // This means that unfortunately, we can pass a null physics actor to Simulate!
1562 // Make sure we don't do that!
1563 SceneObjectPart rootpart = m_rootPart;
1564 if (rootpart != null)
1565 {
1566 if (rootpart.PhysActor != null)
1567 {
1568 if (!IsAttachment)
1569 {
1570 PhysicsVector torque = rootpart.PhysActor.Torque;
1571 return new Vector3(torque.X, torque.Y, torque.Z);
1572 }
1573 }
1574 }
1575 return Vector3.Zero;
1576 }
1577
1578 public void moveToTarget(Vector3 target, float tau)
1579 {
1580 SceneObjectPart rootpart = m_rootPart;
1581 if (rootpart != null)
1582 {
1583 if (rootpart.PhysActor != null)
1584 {
1585 rootpart.PhysActor.PIDTarget = new PhysicsVector(target.X, target.Y, target.Z);
1586 rootpart.PhysActor.PIDTau = tau;
1587 rootpart.PhysActor.PIDActive = true;
1588 }
1589 }
1590 }
1591
1592 public void stopMoveToTarget()
1593 {
1594 SceneObjectPart rootpart = m_rootPart;
1595 if (rootpart != null)
1596 {
1597 if (rootpart.PhysActor != null)
1598 {
1599 rootpart.PhysActor.PIDActive = false;
1600 }
1601 }
1602 }
1603
1604 /// <summary>
1605 /// Set the owner of the root part.
1606 /// </summary>
1607 /// <param name="part"></param>
1608 /// <param name="cAgentID"></param>
1609 /// <param name="cGroupID"></param>
1610 public void SetRootPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID)
1611 {
1612 part.LastOwnerID = part.OwnerID;
1613 part.OwnerID = cAgentID;
1614 part.GroupID = cGroupID;
1615
1616 if (part.OwnerID != cAgentID)
1617 {
1618 // Apply Next Owner Permissions if we're not bypassing permissions
1619 if (!m_scene.Permissions.BypassPermissions())
1620 ApplyNextOwnerPermissions();
1621 }
1622
1623 part.ScheduleFullUpdate();
1624 }
1625
1626 /// <summary>
1627 /// Make a copy of the given part.
1628 /// </summary>
1629 /// <param name="part"></param>
1630 /// <param name="cAgentID"></param>
1631 /// <param name="cGroupID"></param>
1632 public SceneObjectPart CopyPart(SceneObjectPart part, UUID cAgentID, UUID cGroupID, bool userExposed)
1633 {
1634 SceneObjectPart newPart = part.Copy(m_scene.AllocateLocalId(), OwnerID, GroupID, m_parts.Count, userExposed);
1635 newPart.SetParent(this);
1636
1637 lock (m_parts)
1638 {
1639 m_parts.Add(newPart.UUID, newPart);
1640 }
1641
1642 SetPartAsNonRoot(newPart);
1643
1644 return newPart;
1645 }
1646
1647 /// <summary>
1648 /// Reset the UUIDs for all the prims that make up this group.
1649 ///
1650 /// This is called by methods which want to add a new group to an existing scene, in order
1651 /// to ensure that there are no clashes with groups already present.
1652 /// </summary>
1653 public void ResetIDs()
1654 {
1655 // As this is only ever called for prims which are not currently part of the scene (and hence
1656 // not accessible by clients), there should be no need to lock
1657 List<SceneObjectPart> partsList = new List<SceneObjectPart>(m_parts.Values);
1658 m_parts.Clear();
1659 foreach (SceneObjectPart part in partsList)
1660 {
1661 part.ResetIDs(part.LinkNum); // Don't change link nums
1662 m_parts.Add(part.UUID, part);
1663 }
1664 }
1665
1666 /// <summary>
1667 ///
1668 /// </summary>
1669 /// <param name="part"></param>
1670 public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags)
1671 {
1672
1673 remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.ObjectOwner, RootPart.GroupID, RootPart.BaseMask,
1674 RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask,
1675 RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category,
1676 RootPart.CreatorID, RootPart.Name, RootPart.Description);
1677 }
1678
1679 public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID)
1680 {
1681 part.OwnerID = cAgentID;
1682 part.GroupID = cGroupID;
1683 }
1684
1685 #endregion
1686
1687 #region Scheduling
1688
1689 public override void Update()
1690 {
1691 // Check that the group was not deleted before the scheduled update
1692 // FIXME: This is merely a temporary measure to reduce the incidence of failure when
1693 // an object has been deleted from a scene before update was processed.
1694 // A more fundamental overhaul of the update mechanism is required to eliminate all
1695 // the race conditions.
1696 if (m_isDeleted)
1697 return;
1698
1699 // This is what happens when an orphanced link set child prim's
1700 // group was queued when it was linked
1701 //
1702 if (m_rootPart == null)
1703 return;
1704
1705 lock (m_parts)
1706 {
1707 bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0);
1708
1709 //if (IsAttachment)
1710 //{
1711 //foreach (SceneObjectPart part in m_parts.Values)
1712 //{
1713 //part.SendScheduledUpdates();
1714 //}
1715 //return;
1716 //}
1717
1718 if ((Util.GetDistanceTo(lastPhysGroupPos, AbsolutePosition) > 0.02) && UsePhysics)
1719 {
1720 m_rootPart.UpdateFlag = 1;
1721 lastPhysGroupPos = AbsolutePosition;
1722 }
1723 //foreach (SceneObjectPart part in m_parts.Values)
1724 //{
1725 //if (part.UpdateFlag == 0) part.UpdateFlag = 1;
1726 //}
1727
1728 checkAtTargets();
1729
1730 if (((Math.Abs(lastPhysGroupRot.W - GroupRotation.W) > 0.1)
1731 || (Math.Abs(lastPhysGroupRot.X - GroupRotation.X) > 0.1)
1732 || (Math.Abs(lastPhysGroupRot.Y - GroupRotation.Y) > 0.1)
1733 || (Math.Abs(lastPhysGroupRot.Z - GroupRotation.Z) > 0.1))
1734 && UsePhysics)
1735 {
1736 m_rootPart.UpdateFlag = 1;
1737
1738 lastPhysGroupRot = GroupRotation;
1739 }
1740
1741 foreach (SceneObjectPart part in m_parts.Values)
1742 {
1743 part.SendScheduledUpdates();
1744 }
1745 }
1746 }
1747
1748 public void ScheduleFullUpdateToAvatar(ScenePresence presence)
1749 {
1750 RootPart.AddFullUpdateToAvatar(presence);
1751
1752 lock (m_parts)
1753 {
1754 foreach (SceneObjectPart part in m_parts.Values)
1755 {
1756 if (part != RootPart)
1757 part.AddFullUpdateToAvatar(presence);
1758 }
1759 }
1760 }
1761
1762 public void ScheduleTerseUpdateToAvatar(ScenePresence presence)
1763 {
1764 lock (m_parts)
1765 {
1766 foreach (SceneObjectPart part in m_parts.Values)
1767 {
1768 part.AddTerseUpdateToAvatar(presence);
1769 }
1770 }
1771 }
1772
1773 /// <summary>
1774 /// Schedule a full update for this scene object
1775 /// </summary>
1776 public void ScheduleGroupForFullUpdate()
1777 {
1778 checkAtTargets();
1779 RootPart.ScheduleFullUpdate();
1780
1781 lock (m_parts)
1782 {
1783 foreach (SceneObjectPart part in m_parts.Values)
1784 {
1785 if (part != RootPart)
1786 part.ScheduleFullUpdate();
1787 }
1788 }
1789 }
1790
1791 /// <summary>
1792 /// Schedule a terse update for this scene object
1793 /// </summary>
1794 public void ScheduleGroupForTerseUpdate()
1795 {
1796 lock (m_parts)
1797 {
1798 foreach (SceneObjectPart part in m_parts.Values)
1799 {
1800 part.ScheduleTerseUpdate();
1801 }
1802 }
1803 }
1804
1805 /// <summary>
1806 /// Immediately send a full update for this scene object.
1807 /// </summary>
1808 public void SendGroupFullUpdate()
1809 {
1810 if (IsDeleted)
1811 return;
1812
1813 RootPart.SendFullUpdateToAllClients();
1814
1815 lock (m_parts)
1816 {
1817 foreach (SceneObjectPart part in m_parts.Values)
1818 {
1819 if (part != RootPart)
1820 part.SendFullUpdateToAllClients();
1821 }
1822 }
1823 }
1824
1825 /// <summary>
1826 /// Immediately send an update for this scene object's root prim only.
1827 /// This is for updates regarding the object as a whole, and none of its parts in particular.
1828 /// Note: this may not be cused by opensim (it probably should) but it's used by
1829 /// external modules.
1830 /// </summary>
1831 public void SendGroupRootUpdate()
1832 {
1833 if (IsDeleted)
1834 return;
1835
1836 RootPart.SendFullUpdateToAllClients();
1837 }
1838
1839 public void QueueForUpdateCheck()
1840 {
1841 if (m_scene == null) // Need to check here as it's null during object creation
1842 return;
1843 m_scene.m_sceneGraph.AddToUpdateList(this);
1844 }
1845
1846 /// <summary>
1847 /// Immediately send a terse update for this scene object.
1848 /// </summary>
1849 public void SendGroupTerseUpdate()
1850 {
1851 if (IsDeleted)
1852 return;
1853
1854 lock (m_parts)
1855 {
1856 foreach (SceneObjectPart part in m_parts.Values)
1857 {
1858 part.SendTerseUpdateToAllClients();
1859 }
1860 }
1861 }
1862
1863 #endregion
1864
1865 #region SceneGroupPart Methods
1866
1867 /// <summary>
1868 /// Get the child part by LinkNum
1869 /// </summary>
1870 /// <param name="linknum"></param>
1871 /// <returns>null if no child part with that linknum or child part</returns>
1872 public SceneObjectPart GetLinkNumPart(int linknum)
1873 {
1874 lock (m_parts)
1875 {
1876 foreach (SceneObjectPart part in m_parts.Values)
1877 {
1878 if (part.LinkNum == linknum)
1879 {
1880 return part;
1881 }
1882 }
1883 }
1884
1885 return null;
1886 }
1887
1888 /// <summary>
1889 /// Get a child part with a given UUID
1890 /// </summary>
1891 /// <param name="primID"></param>
1892 /// <returns>null if a child part with the primID was not found</returns>
1893 public SceneObjectPart GetChildPart(UUID primID)
1894 {
1895 SceneObjectPart childPart = null;
1896 if (m_parts.ContainsKey(primID))
1897 {
1898 childPart = m_parts[primID];
1899 }
1900 return childPart;
1901 }
1902
1903 /// <summary>
1904 /// Get a child part with a given local ID
1905 /// </summary>
1906 /// <param name="localID"></param>
1907 /// <returns>null if a child part with the local ID was not found</returns>
1908 public SceneObjectPart GetChildPart(uint localID)
1909 {
1910 //m_log.DebugFormat("Entered looking for {0}", localID);
1911 lock (m_parts)
1912 {
1913 foreach (SceneObjectPart part in m_parts.Values)
1914 {
1915 //m_log.DebugFormat("Found {0}", part.LocalId);
1916 if (part.LocalId == localID)
1917 {
1918 return part;
1919 }
1920 }
1921 }
1922
1923 return null;
1924 }
1925
1926 /// <summary>
1927 /// Does this group contain the child prim
1928 /// should be able to remove these methods once we have a entity index in scene
1929 /// </summary>
1930 /// <param name="primID"></param>
1931 /// <returns></returns>
1932 public bool HasChildPrim(UUID primID)
1933 {
1934 if (m_parts.ContainsKey(primID))
1935 {
1936 return true;
1937 }
1938
1939 return false;
1940 }
1941
1942 /// <summary>
1943 /// Does this group contain the child prim
1944 /// should be able to remove these methods once we have a entity index in scene
1945 /// </summary>
1946 /// <param name="localID"></param>
1947 /// <returns></returns>
1948 public bool HasChildPrim(uint localID)
1949 {
1950 //m_log.DebugFormat("Entered HasChildPrim looking for {0}", localID);
1951 lock (m_parts)
1952 {
1953 foreach (SceneObjectPart part in m_parts.Values)
1954 {
1955 //m_log.DebugFormat("Found {0}", part.LocalId);
1956 if (part.LocalId == localID)
1957 {
1958 return true;
1959 }
1960 }
1961 }
1962
1963 return false;
1964 }
1965
1966 #endregion
1967
1968 #region Packet Handlers
1969
1970 /// <summary>
1971 /// Link the prims in a given group to this group
1972 /// </summary>
1973 /// <param name="objectGroup">The group of prims which should be linked to this group</param>
1974 public void LinkToGroup(SceneObjectGroup objectGroup)
1975 {
1976 if (objectGroup.RootPart.UpdateFlag > 0)
1977 {
1978 // I've never actually seen this happen, though I think it's theoretically possible
1979 m_log.WarnFormat(
1980 "[SCENE OBJECT GROUP]: Aborted linking {0}, {1} to {2}, {3} as it has yet to finish delinking",
1981 objectGroup.RootPart.Name, objectGroup.RootPart.UUID, RootPart.Name, RootPart.UUID);
1982
1983 return;
1984 }
1985
1986// m_log.DebugFormat(
1987// "[SCENE OBJECT GROUP]: Linking group with root part {0}, {1} to group with root part {2}, {3}",
1988// objectGroup.RootPart.Name, objectGroup.RootPart.UUID, RootPart.Name, RootPart.UUID);
1989
1990 SceneObjectPart linkPart = objectGroup.m_rootPart;
1991
1992 Vector3 oldGroupPosition = linkPart.GroupPosition;
1993 Quaternion oldRootRotation = linkPart.RotationOffset;
1994
1995 linkPart.OffsetPosition = linkPart.GroupPosition - AbsolutePosition;
1996 linkPart.GroupPosition = AbsolutePosition;
1997 Vector3 axPos = linkPart.OffsetPosition;
1998
1999 Quaternion parentRot = m_rootPart.RotationOffset;
2000 axPos *= Quaternion.Inverse(parentRot);
2001
2002 linkPart.OffsetPosition = axPos;
2003 Quaternion oldRot = linkPart.RotationOffset;
2004 Quaternion newRot = Quaternion.Inverse(parentRot) * oldRot;
2005 linkPart.RotationOffset = newRot;
2006
2007 linkPart.ParentID = m_rootPart.LocalId;
2008 if (m_rootPart.LinkNum == 0)
2009 m_rootPart.LinkNum = 1;
2010
2011 lock (m_parts)
2012 {
2013 m_parts.Add(linkPart.UUID, linkPart);
2014
2015 // Insert in terms of link numbers, the new links
2016 // before the current ones (with the exception of
2017 // the root prim. Shuffle the old ones up
2018 foreach (KeyValuePair<UUID, SceneObjectPart> kvp in m_parts)
2019 {
2020 if (kvp.Value.LinkNum != 1)
2021 {
2022 // Don't update root prim link number
2023 kvp.Value.LinkNum += objectGroup.PrimCount;
2024 }
2025 }
2026
2027 linkPart.LinkNum = 2;
2028
2029 linkPart.SetParent(this);
2030 linkPart.AddFlag(PrimFlags.CreateSelected);
2031
2032 //if (linkPart.PhysActor != null)
2033 //{
2034 // m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor);
2035
2036 //linkPart.PhysActor = null;
2037 //}
2038
2039 //TODO: rest of parts
2040 int linkNum = 3;
2041 foreach (SceneObjectPart part in objectGroup.Children.Values)
2042 {
2043 if (part.UUID != objectGroup.m_rootPart.UUID)
2044 {
2045 LinkNonRootPart(part, oldGroupPosition, oldRootRotation, linkNum++);
2046 }
2047 part.ClearUndoState();
2048 }
2049 }
2050
2051 m_scene.UnlinkSceneObject(objectGroup.UUID, true);
2052 objectGroup.m_isDeleted = true;
2053
2054 lock (objectGroup.m_parts)
2055 {
2056 objectGroup.m_parts.Clear();
2057 }
2058
2059 // Can't do this yet since backup still makes use of the root part without any synchronization
2060// objectGroup.m_rootPart = null;
2061
2062 AttachToBackup();
2063 HasGroupChanged = true;
2064 ScheduleGroupForFullUpdate();
2065 }
2066
2067 /// <summary>
2068 /// Delink the given prim from this group. The delinked prim is established as
2069 /// an independent SceneObjectGroup.
2070 /// </summary>
2071 /// <param name="partID"></param>
2072 public void DelinkFromGroup(uint partID)
2073 {
2074 DelinkFromGroup(partID, true);
2075 }
2076
2077 /// <summary>
2078 /// Delink the given prim from this group. The delinked prim is established as
2079 /// an independent SceneObjectGroup.
2080 /// </summary>
2081 /// <param name="partID"></param>
2082 /// <param name="sendEvents"></param>
2083 public void DelinkFromGroup(uint partID, bool sendEvents)
2084 {
2085 SceneObjectPart linkPart = GetChildPart(partID);
2086
2087 if (null != linkPart)
2088 {
2089 linkPart.ClearUndoState();
2090// m_log.DebugFormat(
2091// "[SCENE OBJECT GROUP]: Delinking part {0}, {1} from group with root part {2}, {3}",
2092// linkPart.Name, linkPart.UUID, RootPart.Name, RootPart.UUID);
2093
2094 Quaternion worldRot = linkPart.GetWorldRotation();
2095
2096 // Remove the part from this object
2097 lock (m_parts)
2098 {
2099 m_parts.Remove(linkPart.UUID);
2100 }
2101
2102 if (m_parts.Count == 1 && RootPart != null) //Single prim is left
2103 RootPart.LinkNum = 0;
2104 else
2105 {
2106 foreach (SceneObjectPart p in m_parts.Values)
2107 {
2108 if (p.LinkNum > linkPart.LinkNum)
2109 p.LinkNum--;
2110 }
2111 }
2112
2113 linkPart.ParentID = 0;
2114 linkPart.LinkNum = 0;
2115
2116 if (linkPart.PhysActor != null)
2117 {
2118 m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor);
2119 }
2120
2121 // We need to reset the child part's position
2122 // ready for life as a separate object after being a part of another object
2123 Quaternion parentRot = m_rootPart.RotationOffset;
2124
2125 Vector3 axPos = linkPart.OffsetPosition;
2126
2127 axPos *= parentRot;
2128 linkPart.OffsetPosition = new Vector3(axPos.X, axPos.Y, axPos.Z);
2129 linkPart.GroupPosition = AbsolutePosition + linkPart.OffsetPosition;
2130 linkPart.OffsetPosition = new Vector3(0, 0, 0);
2131
2132 linkPart.RotationOffset = worldRot;
2133
2134 SceneObjectGroup objectGroup = new SceneObjectGroup(linkPart);
2135
2136 m_scene.AddNewSceneObject(objectGroup, true);
2137
2138 if (sendEvents)
2139 linkPart.TriggerScriptChangedEvent(Changed.LINK);
2140
2141 linkPart.Rezzed = RootPart.Rezzed;
2142
2143 HasGroupChanged = true;
2144 ScheduleGroupForFullUpdate();
2145 }
2146 else
2147 {
2148 m_log.InfoFormat("[SCENE OBJECT GROUP]: " +
2149 "DelinkFromGroup(): Child prim {0} not found in object {1}, {2}",
2150 partID, LocalId, UUID);
2151 }
2152 }
2153
2154 /// <summary>
2155 /// Stop this object from being persisted over server restarts.
2156 /// </summary>
2157 /// <param name="objectGroup"></param>
2158 public void DetachFromBackup()
2159 {
2160 if (m_isBackedUp)
2161 m_scene.EventManager.OnBackup -= ProcessBackup;
2162
2163 m_isBackedUp = false;
2164 }
2165
2166 private void LinkNonRootPart(SceneObjectPart part, Vector3 oldGroupPosition, Quaternion oldGroupRotation, int linkNum)
2167 {
2168
2169 Quaternion parentRot = oldGroupRotation;
2170 Quaternion oldRot = part.RotationOffset;
2171 Quaternion worldRot = parentRot * oldRot;
2172
2173 parentRot = oldGroupRotation;
2174
2175 Vector3 axPos = part.OffsetPosition;
2176
2177 axPos *= parentRot;
2178 part.OffsetPosition = axPos;
2179 part.GroupPosition = oldGroupPosition + part.OffsetPosition;
2180 part.OffsetPosition = Vector3.Zero;
2181 part.RotationOffset = worldRot;
2182
2183 part.SetParent(this);
2184 part.ParentID = m_rootPart.LocalId;
2185
2186 // Caller locks m_parts for us
2187 m_parts.Add(part.UUID, part);
2188
2189 part.LinkNum = linkNum;
2190
2191
2192 part.OffsetPosition = part.GroupPosition - AbsolutePosition;
2193
2194 Quaternion rootRotation = m_rootPart.RotationOffset;
2195
2196 Vector3 pos = part.OffsetPosition;
2197 pos *= Quaternion.Inverse(rootRotation);
2198 part.OffsetPosition = pos;
2199
2200 parentRot = m_rootPart.RotationOffset;
2201 oldRot = part.RotationOffset;
2202 Quaternion newRot = Quaternion.Inverse(parentRot) * oldRot;
2203 part.RotationOffset = newRot;
2204 }
2205
2206 /// <summary>
2207 /// If object is physical, apply force to move it around
2208 /// If object is not physical, just put it at the resulting location
2209 /// </summary>
2210 /// <param name="offset">Always seems to be 0,0,0, so ignoring</param>
2211 /// <param name="pos">New position. We do the math here to turn it into a force</param>
2212 /// <param name="remoteClient"></param>
2213 public void GrabMovement(Vector3 offset, Vector3 pos, IClientAPI remoteClient)
2214 {
2215 if (m_scene.EventManager.TriggerGroupMove(UUID, pos))
2216 {
2217 if (m_rootPart.PhysActor != null)
2218 {
2219 if (m_rootPart.PhysActor.IsPhysical)
2220 {
2221 Vector3 llmoveforce = pos - AbsolutePosition;
2222 PhysicsVector grabforce = new PhysicsVector(llmoveforce.X, llmoveforce.Y, llmoveforce.Z);
2223 grabforce = (grabforce / 10) * m_rootPart.PhysActor.Mass;
2224 m_rootPart.PhysActor.AddForce(grabforce,true);
2225 m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
2226 }
2227 else
2228 {
2229 //NonPhysicalGrabMovement(pos);
2230 }
2231 }
2232 else
2233 {
2234 //NonPhysicalGrabMovement(pos);
2235 }
2236 }
2237 }
2238
2239 public void NonPhysicalGrabMovement(Vector3 pos)
2240 {
2241 AbsolutePosition = pos;
2242 m_rootPart.SendTerseUpdateToAllClients();
2243 }
2244
2245 /// <summary>
2246 /// Return metadata about a prim (name, description, sale price, etc.)
2247 /// </summary>
2248 /// <param name="client"></param>
2249 public void GetProperties(IClientAPI client)
2250 {
2251 m_rootPart.GetProperties(client);
2252 }
2253
2254 /// <summary>
2255 /// Set the name of a prim
2256 /// </summary>
2257 /// <param name="name"></param>
2258 /// <param name="localID"></param>
2259 public void SetPartName(string name, uint localID)
2260 {
2261 SceneObjectPart part = GetChildPart(localID);
2262 if (part != null)
2263 {
2264 part.Name = name;
2265 }
2266 }
2267
2268 public void SetPartDescription(string des, uint localID)
2269 {
2270 SceneObjectPart part = GetChildPart(localID);
2271 if (part != null)
2272 {
2273 part.Description = des;
2274 }
2275 }
2276
2277 public void SetPartText(string text, uint localID)
2278 {
2279 SceneObjectPart part = GetChildPart(localID);
2280 if (part != null)
2281 {
2282 part.SetText(text);
2283 }
2284 }
2285
2286 public void SetPartText(string text, UUID partID)
2287 {
2288 SceneObjectPart part = GetChildPart(partID);
2289 if (part != null)
2290 {
2291 part.SetText(text);
2292 }
2293 }
2294
2295 public string GetPartName(uint localID)
2296 {
2297 SceneObjectPart part = GetChildPart(localID);
2298 if (part != null)
2299 {
2300 return part.Name;
2301 }
2302 return String.Empty;
2303 }
2304
2305 public string GetPartDescription(uint localID)
2306 {
2307 SceneObjectPart part = GetChildPart(localID);
2308 if (part != null)
2309 {
2310 return part.Description;
2311 }
2312 return String.Empty;
2313 }
2314
2315 /// <summary>
2316 /// Update prim flags for this group.
2317 /// </summary>
2318 /// <param name="localID"></param>
2319 /// <param name="type"></param>
2320 /// <param name="inUse"></param>
2321 /// <param name="data"></param>
2322 public void UpdatePrimFlags(uint localID, bool UsePhysics, bool IsTemporary, bool IsPhantom, bool IsVolumeDetect)
2323 {
2324 SceneObjectPart selectionPart = GetChildPart(localID);
2325
2326 if (IsTemporary)
2327 {
2328 DetachFromBackup();
2329 // Remove from database and parcel prim count
2330 //
2331 m_scene.DeleteFromStorage(UUID);
2332 m_scene.EventManager.TriggerParcelPrimCountTainted();
2333 }
2334
2335 if (selectionPart != null)
2336 {
2337 lock (m_parts)
2338 {
2339 foreach (SceneObjectPart part in m_parts.Values)
2340 {
2341 if (part.Scale.X > 10.0 || part.Scale.Y > 10.0 || part.Scale.Z > 10.0)
2342 {
2343 UsePhysics = false; // Reset physics
2344 break;
2345 }
2346 }
2347
2348 foreach (SceneObjectPart part in m_parts.Values)
2349 {
2350 part.UpdatePrimFlags(UsePhysics, IsTemporary, IsPhantom, IsVolumeDetect);
2351 }
2352 }
2353 }
2354 }
2355
2356 public void UpdateExtraParam(uint localID, ushort type, bool inUse, byte[] data)
2357 {
2358 SceneObjectPart part = GetChildPart(localID);
2359 if (part != null)
2360 {
2361 part.UpdateExtraParam(type, inUse, data);
2362 }
2363 }
2364
2365 /// <summary>
2366 /// Get the parts of this scene object
2367 /// </summary>
2368 /// <returns></returns>
2369 public SceneObjectPart[] GetParts()
2370 {
2371 int numParts = Children.Count;
2372 SceneObjectPart[] partArray = new SceneObjectPart[numParts];
2373 Children.Values.CopyTo(partArray, 0);
2374 return partArray;
2375 }
2376
2377 /// <summary>
2378 /// Update the texture entry for this part
2379 /// </summary>
2380 /// <param name="localID"></param>
2381 /// <param name="textureEntry"></param>
2382 public void UpdateTextureEntry(uint localID, byte[] textureEntry)
2383 {
2384 SceneObjectPart part = GetChildPart(localID);
2385 if (part != null)
2386 {
2387 part.UpdateTextureEntry(textureEntry);
2388 }
2389 }
2390
2391 public void UpdatePermissions(UUID AgentID, byte field, uint localID,
2392 uint mask, byte addRemTF)
2393 {
2394 foreach (SceneObjectPart part in m_parts.Values)
2395 part.UpdatePermissions(AgentID, field, localID, mask,
2396 addRemTF);
2397
2398 HasGroupChanged = true;
2399 }
2400
2401 #endregion
2402
2403 #region Shape
2404
2405 /// <summary>
2406 ///
2407 /// </summary>
2408 /// <param name="shapeBlock"></param>
2409 public void UpdateShape(ObjectShapePacket.ObjectDataBlock shapeBlock, uint localID)
2410 {
2411 SceneObjectPart part = GetChildPart(localID);
2412 if (part != null)
2413 {
2414 part.UpdateShape(shapeBlock);
2415
2416 if (part.PhysActor != null)
2417 m_scene.PhysicsScene.AddPhysicsActorTaint(part.PhysActor);
2418 }
2419 }
2420
2421 #endregion
2422
2423 #region Resize
2424
2425 /// <summary>
2426 /// Resize the given part
2427 /// </summary>
2428 /// <param name="scale"></param>
2429 /// <param name="localID"></param>
2430 public void Resize(Vector3 scale, uint localID)
2431 {
2432 if (scale.X > m_scene.m_maxNonphys)
2433 scale.X = m_scene.m_maxNonphys;
2434 if (scale.Y > m_scene.m_maxNonphys)
2435 scale.Y = m_scene.m_maxNonphys;
2436 if (scale.Z > m_scene.m_maxNonphys)
2437 scale.Z = m_scene.m_maxNonphys;
2438
2439 SceneObjectPart part = GetChildPart(localID);
2440 if (part != null)
2441 {
2442 part.Resize(scale);
2443 if (part.PhysActor != null)
2444 {
2445 if (part.PhysActor.IsPhysical)
2446 {
2447 if (scale.X > m_scene.m_maxPhys)
2448 scale.X = m_scene.m_maxPhys;
2449 if (scale.Y > m_scene.m_maxPhys)
2450 scale.Y = m_scene.m_maxPhys;
2451 if (scale.Z > m_scene.m_maxPhys)
2452 scale.Z = m_scene.m_maxPhys;
2453 }
2454 part.PhysActor.Size =
2455 new PhysicsVector(scale.X, scale.Y, scale.Z);
2456 m_scene.PhysicsScene.AddPhysicsActorTaint(part.PhysActor);
2457 }
2458 //if (part.UUID != m_rootPart.UUID)
2459
2460 HasGroupChanged = true;
2461 ScheduleGroupForFullUpdate();
2462
2463 //if (part.UUID == m_rootPart.UUID)
2464 //{
2465 //if (m_rootPart.PhysActor != null)
2466 //{
2467 //m_rootPart.PhysActor.Size =
2468 //new PhysicsVector(m_rootPart.Scale.X, m_rootPart.Scale.Y, m_rootPart.Scale.Z);
2469 //m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
2470 //}
2471 //}
2472 }
2473 }
2474
2475 public void GroupResize(Vector3 scale, uint localID)
2476 {
2477 SceneObjectPart part = GetChildPart(localID);
2478 if (part != null)
2479 {
2480 if (scale.X > m_scene.m_maxNonphys)
2481 scale.X = m_scene.m_maxNonphys;
2482 if (scale.Y > m_scene.m_maxNonphys)
2483 scale.Y = m_scene.m_maxNonphys;
2484 if (scale.Z > m_scene.m_maxNonphys)
2485 scale.Z = m_scene.m_maxNonphys;
2486 if (part.PhysActor != null && part.PhysActor.IsPhysical)
2487 {
2488 if (scale.X > m_scene.m_maxPhys)
2489 scale.X = m_scene.m_maxPhys;
2490 if (scale.Y > m_scene.m_maxPhys)
2491 scale.Y = m_scene.m_maxPhys;
2492 if (scale.Z > m_scene.m_maxPhys)
2493 scale.Z = m_scene.m_maxPhys;
2494 }
2495 float x = (scale.X / part.Scale.X);
2496 float y = (scale.Y / part.Scale.Y);
2497 float z = (scale.Z / part.Scale.Z);
2498
2499 lock (m_parts)
2500 {
2501 if (x > 1.0f || y > 1.0f || z > 1.0f)
2502 {
2503 foreach (SceneObjectPart obPart in m_parts.Values)
2504 {
2505 if (obPart.UUID != m_rootPart.UUID)
2506 {
2507 Vector3 oldSize = new Vector3(obPart.Scale);
2508
2509 float f = 1.0f;
2510 float a = 1.0f;
2511
2512 if (part.PhysActor != null && part.PhysActor.IsPhysical)
2513 {
2514 if (oldSize.X*x > m_scene.m_maxPhys)
2515 {
2516 f = m_scene.m_maxPhys / oldSize.X;
2517 a = f / x;
2518 x *= a;
2519 y *= a;
2520 z *= a;
2521 }
2522 if (oldSize.Y*y > m_scene.m_maxPhys)
2523 {
2524 f = m_scene.m_maxPhys / oldSize.Y;
2525 a = f / y;
2526 x *= a;
2527 y *= a;
2528 z *= a;
2529 }
2530 if (oldSize.Z*z > m_scene.m_maxPhys)
2531 {
2532 f = m_scene.m_maxPhys / oldSize.Z;
2533 a = f / z;
2534 x *= a;
2535 y *= a;
2536 z *= a;
2537 }
2538 }
2539 else
2540 {
2541 if (oldSize.X*x > m_scene.m_maxNonphys)
2542 {
2543 f = m_scene.m_maxNonphys / oldSize.X;
2544 a = f / x;
2545 x *= a;
2546 y *= a;
2547 z *= a;
2548 }
2549 if (oldSize.Y*y > m_scene.m_maxNonphys)
2550 {
2551 f = m_scene.m_maxNonphys / oldSize.Y;
2552 a = f / y;
2553 x *= a;
2554 y *= a;
2555 z *= a;
2556 }
2557 if (oldSize.Z*z > m_scene.m_maxNonphys)
2558 {
2559 f = m_scene.m_maxNonphys / oldSize.Z;
2560 a = f / z;
2561 x *= a;
2562 y *= a;
2563 z *= a;
2564 }
2565 }
2566 }
2567 }
2568 }
2569 }
2570
2571 Vector3 prevScale = part.Scale;
2572 prevScale.X *= x;
2573 prevScale.Y *= y;
2574 prevScale.Z *= z;
2575 part.Resize(prevScale);
2576
2577 lock (m_parts)
2578 {
2579 foreach (SceneObjectPart obPart in m_parts.Values)
2580 {
2581 if (obPart.UUID != m_rootPart.UUID)
2582 {
2583 Vector3 currentpos = new Vector3(obPart.OffsetPosition);
2584 currentpos.X *= x;
2585 currentpos.Y *= y;
2586 currentpos.Z *= z;
2587 Vector3 newSize = new Vector3(obPart.Scale);
2588 newSize.X *= x;
2589 newSize.Y *= y;
2590 newSize.Z *= z;
2591 obPart.Resize(newSize);
2592 obPart.UpdateOffSet(currentpos);
2593 }
2594 }
2595 }
2596
2597 if (part.PhysActor != null)
2598 {
2599 part.PhysActor.Size =
2600 new PhysicsVector(prevScale.X, prevScale.Y, prevScale.Z);
2601 m_scene.PhysicsScene.AddPhysicsActorTaint(part.PhysActor);
2602 }
2603
2604 HasGroupChanged = true;
2605 ScheduleGroupForTerseUpdate();
2606 }
2607 }
2608
2609 #endregion
2610
2611 #region Position
2612
2613 /// <summary>
2614 /// Move this scene object
2615 /// </summary>
2616 /// <param name="pos"></param>
2617 public void UpdateGroupPosition(Vector3 pos)
2618 {
2619 if (m_scene.EventManager.TriggerGroupMove(UUID, pos))
2620 {
2621 if (IsAttachment)
2622 {
2623 m_rootPart.AttachedPos = pos;
2624 }
2625
2626 AbsolutePosition = pos;
2627
2628 HasGroupChanged = true;
2629 }
2630
2631 //we need to do a terse update even if the move wasn't allowed
2632 // so that the position is reset in the client (the object snaps back)
2633 ScheduleGroupForTerseUpdate();
2634 }
2635
2636 /// <summary>
2637 /// Update the position of a single part of this scene object
2638 /// </summary>
2639 /// <param name="pos"></param>
2640 /// <param name="localID"></param>
2641 public void UpdateSinglePosition(Vector3 pos, uint localID)
2642 {
2643 SceneObjectPart part = GetChildPart(localID);
2644
2645 if (part != null)
2646 {
2647 if (part.UUID == m_rootPart.UUID)
2648 {
2649 UpdateRootPosition(pos);
2650 }
2651 else
2652 {
2653 part.UpdateOffSet(pos);
2654 }
2655
2656 HasGroupChanged = true;
2657 }
2658 }
2659
2660 /// <summary>
2661 ///
2662 /// </summary>
2663 /// <param name="pos"></param>
2664 private void UpdateRootPosition(Vector3 pos)
2665 {
2666 Vector3 newPos = new Vector3(pos.X, pos.Y, pos.Z);
2667 Vector3 oldPos =
2668 new Vector3(AbsolutePosition.X + m_rootPart.OffsetPosition.X,
2669 AbsolutePosition.Y + m_rootPart.OffsetPosition.Y,
2670 AbsolutePosition.Z + m_rootPart.OffsetPosition.Z);
2671 Vector3 diff = oldPos - newPos;
2672 Vector3 axDiff = new Vector3(diff.X, diff.Y, diff.Z);
2673 Quaternion partRotation = m_rootPart.RotationOffset;
2674 axDiff *= Quaternion.Inverse(partRotation);
2675 diff = axDiff;
2676
2677 lock (m_parts)
2678 {
2679 foreach (SceneObjectPart obPart in m_parts.Values)
2680 {
2681 if (obPart.UUID != m_rootPart.UUID)
2682 {
2683 obPart.OffsetPosition = obPart.OffsetPosition + diff;
2684 }
2685 }
2686 }
2687
2688 AbsolutePosition = newPos;
2689
2690 HasGroupChanged = true;
2691 ScheduleGroupForTerseUpdate();
2692 }
2693
2694 public void OffsetForNewRegion(Vector3 offset)
2695 {
2696 m_rootPart.GroupPosition = offset;
2697 }
2698
2699 #endregion
2700
2701 #region Rotation
2702
2703 /// <summary>
2704 ///
2705 /// </summary>
2706 /// <param name="rot"></param>
2707 public void UpdateGroupRotation(Quaternion rot)
2708 {
2709 m_rootPart.UpdateRotation(rot);
2710 if (m_rootPart.PhysActor != null)
2711 {
2712 m_rootPart.PhysActor.Orientation = m_rootPart.RotationOffset;
2713 m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
2714 }
2715
2716 HasGroupChanged = true;
2717 ScheduleGroupForTerseUpdate();
2718 }
2719
2720 /// <summary>
2721 ///
2722 /// </summary>
2723 /// <param name="pos"></param>
2724 /// <param name="rot"></param>
2725 public void UpdateGroupRotation(Vector3 pos, Quaternion rot)
2726 {
2727 m_rootPart.UpdateRotation(rot);
2728 if (m_rootPart.PhysActor != null)
2729 {
2730 m_rootPart.PhysActor.Orientation = m_rootPart.RotationOffset;
2731 m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
2732 }
2733 AbsolutePosition = pos;
2734
2735 HasGroupChanged = true;
2736 ScheduleGroupForTerseUpdate();
2737 }
2738
2739 /// <summary>
2740 ///
2741 /// </summary>
2742 /// <param name="rot"></param>
2743 /// <param name="localID"></param>
2744 public void UpdateSingleRotation(Quaternion rot, uint localID)
2745 {
2746 SceneObjectPart part = GetChildPart(localID);
2747 if (part != null)
2748 {
2749 if (part.UUID == m_rootPart.UUID)
2750 {
2751 UpdateRootRotation(rot);
2752 }
2753 else
2754 {
2755 part.UpdateRotation(rot);
2756 }
2757 }
2758 }
2759
2760 /// <summary>
2761 ///
2762 /// </summary>
2763 /// <param name="rot"></param>
2764 private void UpdateRootRotation(Quaternion rot)
2765 {
2766 Quaternion axRot = rot;
2767 Quaternion oldParentRot = m_rootPart.RotationOffset;
2768
2769 m_rootPart.UpdateRotation(rot);
2770 if (m_rootPart.PhysActor != null)
2771 {
2772 m_rootPart.PhysActor.Orientation = m_rootPart.RotationOffset;
2773 m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
2774 }
2775
2776 lock (m_parts)
2777 {
2778 foreach (SceneObjectPart prim in m_parts.Values)
2779 {
2780 if (prim.UUID != m_rootPart.UUID)
2781 {
2782 Vector3 axPos = prim.OffsetPosition;
2783 axPos *= oldParentRot;
2784 axPos *= Quaternion.Inverse(axRot);
2785 prim.OffsetPosition = axPos;
2786 Quaternion primsRot = prim.RotationOffset;
2787 Quaternion newRot = primsRot * oldParentRot;
2788 newRot *= Quaternion.Inverse(axRot);
2789 prim.RotationOffset = newRot;
2790 prim.ScheduleTerseUpdate();
2791 }
2792 }
2793 }
2794
2795 m_rootPart.ScheduleTerseUpdate();
2796 }
2797
2798 #endregion
2799
2800 internal void SetAxisRotation(int axis, int rotate10)
2801 {
2802 bool setX = false;
2803 bool setY = false;
2804 bool setZ = false;
2805
2806 int xaxis = 2;
2807 int yaxis = 4;
2808 int zaxis = 8;
2809
2810 if (m_rootPart != null)
2811 {
2812 setX = ((axis & xaxis) != 0) ? true : false;
2813 setY = ((axis & yaxis) != 0) ? true : false;
2814 setZ = ((axis & zaxis) != 0) ? true : false;
2815
2816 float setval = (rotate10 > 0) ? 1f : 0f;
2817
2818 if (setX)
2819 m_rootPart.RotationAxis.X = setval;
2820 if (setY)
2821 m_rootPart.RotationAxis.Y = setval;
2822 if (setZ)
2823 m_rootPart.RotationAxis.Z = setval;
2824
2825 if (setX || setY || setZ)
2826 {
2827 m_rootPart.SetPhysicsAxisRotation();
2828 }
2829
2830 }
2831 }
2832
2833 public int registerTargetWaypoint(Vector3 target, float tolerance)
2834 {
2835 scriptPosTarget waypoint = new scriptPosTarget();
2836 waypoint.targetPos = target;
2837 waypoint.tolerance = tolerance;
2838 uint handle = m_scene.AllocateLocalId();
2839 lock (m_targets)
2840 {
2841 m_targets.Add(handle, waypoint);
2842 }
2843 return (int)handle;
2844 }
2845
2846 public void unregisterTargetWaypoint(int handle)
2847 {
2848 lock (m_targets)
2849 {
2850 if (m_targets.ContainsKey((uint)handle))
2851 m_targets.Remove((uint)handle);
2852 }
2853 }
2854
2855 private void checkAtTargets()
2856 {
2857 if (m_scriptListens_atTarget || m_scriptListens_notAtTarget)
2858 {
2859 if (m_targets.Count > 0)
2860 {
2861 bool at_target = false;
2862 //Vector3 targetPos;
2863 //uint targetHandle;
2864 Dictionary<uint, scriptPosTarget> atTargets = new Dictionary<uint, scriptPosTarget>();
2865 lock (m_targets)
2866 {
2867 foreach (uint idx in m_targets.Keys)
2868 {
2869 scriptPosTarget target = m_targets[idx];
2870 if (Util.GetDistanceTo(target.targetPos, m_rootPart.GroupPosition) <= target.tolerance)
2871 {
2872 // trigger at_target
2873 if (m_scriptListens_atTarget)
2874 {
2875 // Reusing att.tolerance to hold the index of the target in the targets dictionary
2876 // to avoid deadlocking the sim.
2877 at_target = true;
2878 scriptPosTarget att = new scriptPosTarget();
2879 att.targetPos = target.targetPos;
2880 att.tolerance = (float)idx;
2881 atTargets.Add(idx, att);
2882 }
2883 }
2884 }
2885 }
2886 if (atTargets.Count > 0)
2887 {
2888 uint[] localids = new uint[0];
2889 lock (m_parts)
2890 {
2891 localids = new uint[m_parts.Count];
2892 int cntr = 0;
2893 foreach (SceneObjectPart part in m_parts.Values)
2894 {
2895 localids[cntr] = part.LocalId;
2896 cntr++;
2897 }
2898 }
2899 for (int ctr = 0; ctr < localids.Length; ctr++)
2900 {
2901 foreach (uint target in atTargets.Keys)
2902 {
2903 scriptPosTarget att = atTargets[target];
2904 // Reusing att.tolerance to hold the index of the target in the targets dictionary
2905 // to avoid deadlocking the sim.
2906 m_scene.TriggerAtTargetEvent(localids[ctr], (uint)att.tolerance, att.targetPos, m_rootPart.GroupPosition);
2907
2908
2909 }
2910 }
2911 return;
2912 }
2913 if (m_scriptListens_notAtTarget && !at_target)
2914 {
2915 //trigger not_at_target
2916 uint[] localids = new uint[0];
2917 lock (m_parts)
2918 {
2919 localids = new uint[m_parts.Count];
2920 int cntr = 0;
2921 foreach (SceneObjectPart part in m_parts.Values)
2922 {
2923 localids[cntr] = part.LocalId;
2924 cntr++;
2925 }
2926 }
2927 for (int ctr = 0; ctr < localids.Length; ctr++)
2928 {
2929 m_scene.TriggerNotAtTargetEvent(localids[ctr]);
2930 }
2931 }
2932 }
2933 }
2934 }
2935
2936 public float GetMass()
2937 {
2938 float retmass = 0f;
2939 lock (m_parts)
2940 {
2941 foreach (SceneObjectPart part in m_parts.Values)
2942 {
2943 retmass += part.GetMass();
2944 }
2945 }
2946 return retmass;
2947 }
2948
2949 public void CheckSculptAndLoad()
2950 {
2951 lock (m_parts)
2952 {
2953 if (!IsDeleted)
2954 {
2955 if ((RootPart.GetEffectiveObjectFlags() & (uint)PrimFlags.Phantom) == 0)
2956 {
2957 foreach (SceneObjectPart part in m_parts.Values)
2958 {
2959 if (part.Shape.SculptEntry && part.Shape.SculptTexture != UUID.Zero)
2960 {
2961 m_scene.AssetCache.GetAsset(part.Shape.SculptTexture, part.SculptTextureCallback, true);
2962 }
2963 }
2964 }
2965 }
2966 }
2967 }
2968
2969 /// <summary>
2970 /// Set the user group to which this scene object belongs.
2971 /// </summary>
2972 /// <param name="GroupID"></param>
2973 /// <param name="client"></param>
2974 public void SetGroup(UUID GroupID, IClientAPI client)
2975 {
2976 lock (m_parts)
2977 {
2978 foreach (SceneObjectPart part in m_parts.Values)
2979 {
2980 part.SetGroup(GroupID, client);
2981 part.Inventory.ChangeInventoryGroup(GroupID);
2982 }
2983
2984 HasGroupChanged = true;
2985 }
2986
2987 ScheduleGroupForFullUpdate();
2988 }
2989
2990 public void TriggerScriptChangedEvent(Changed val)
2991 {
2992 foreach (SceneObjectPart part in Children.Values)
2993 {
2994 part.TriggerScriptChangedEvent(val);
2995 }
2996 }
2997
2998 public override string ToString()
2999 {
3000 return String.Format("{0} {1} ({2})", Name, UUID, AbsolutePosition);
3001 }
3002
3003 public void SetAttachmentPoint(byte point)
3004 {
3005 lock (m_parts)
3006 {
3007 foreach (SceneObjectPart part in m_parts.Values)
3008 part.SetAttachmentPoint(point);
3009 }
3010 }
3011 }
3012}