aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework/Scenes/SceneGraph.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/SceneGraph.cs')
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneGraph.cs1814
1 files changed, 1814 insertions, 0 deletions
diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs
new file mode 100644
index 0000000..2877dcd
--- /dev/null
+++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs
@@ -0,0 +1,1814 @@
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.Reflection;
31using OpenMetaverse;
32using OpenMetaverse.Packets;
33using log4net;
34using OpenSim.Framework;
35using OpenSim.Region.Framework.Scenes.Types;
36using OpenSim.Region.Physics.Manager;
37
38namespace OpenSim.Region.Framework.Scenes
39{
40 public delegate void PhysicsCrash();
41
42 public delegate void ObjectDuplicateDelegate(EntityBase original, EntityBase clone);
43
44 public delegate void ObjectCreateDelegate(EntityBase obj);
45
46 public delegate void ObjectDeleteDelegate(EntityBase obj);
47
48 /// <summary>
49 /// This class used to be called InnerScene and may not yet truly be a SceneGraph. The non scene graph components
50 /// should be migrated out over time.
51 /// </summary>
52 public class SceneGraph
53 {
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55
56 #region Events
57
58 protected internal event PhysicsCrash UnRecoverableError;
59 private PhysicsCrash handlerPhysicsCrash = null;
60
61 public event ObjectDuplicateDelegate OnObjectDuplicate;
62 public event ObjectCreateDelegate OnObjectCreate;
63 public event ObjectDeleteDelegate OnObjectRemove;
64
65 #endregion
66
67 #region Fields
68
69 protected internal Dictionary<UUID, ScenePresence> ScenePresences = new Dictionary<UUID, ScenePresence>();
70 // SceneObjects is not currently populated or used.
71 //public Dictionary<UUID, SceneObjectGroup> SceneObjects;
72 protected internal EntityManager Entities = new EntityManager();
73// protected internal Dictionary<UUID, EntityBase> Entities = new Dictionary<UUID, EntityBase>();
74 protected internal Dictionary<UUID, ScenePresence> RestorePresences = new Dictionary<UUID, ScenePresence>();
75
76 protected internal BasicQuadTreeNode QuadTree;
77
78 protected RegionInfo m_regInfo;
79 protected Scene m_parentScene;
80 protected List<EntityBase> m_updateList = new List<EntityBase>();
81 protected int m_numRootAgents = 0;
82 protected int m_numPrim = 0;
83 protected int m_numChildAgents = 0;
84 protected int m_physicalPrim = 0;
85
86 protected int m_activeScripts = 0;
87 protected int m_scriptLPS = 0;
88
89 protected internal object m_syncRoot = new object();
90
91 protected internal PhysicsScene _PhyScene;
92
93 #endregion
94
95 protected internal SceneGraph(Scene parent, RegionInfo regInfo)
96 {
97 m_parentScene = parent;
98 m_regInfo = regInfo;
99 QuadTree = new BasicQuadTreeNode(null, "/0/", 0, 0, (short)Constants.RegionSize, (short)Constants.RegionSize);
100 QuadTree.Subdivide();
101 QuadTree.Subdivide();
102 }
103
104 public PhysicsScene PhysicsScene
105 {
106 get { return _PhyScene; }
107 set
108 {
109 // If we're not doing the initial set
110 // Then we've got to remove the previous
111 // event handler
112
113 if (_PhyScene != null)
114 _PhyScene.OnPhysicsCrash -= physicsBasedCrash;
115
116 _PhyScene = value;
117
118 if (_PhyScene != null)
119 _PhyScene.OnPhysicsCrash += physicsBasedCrash;
120 }
121 }
122
123 protected internal void Close()
124 {
125 lock (ScenePresences)
126 {
127 ScenePresences.Clear();
128 }
129
130 Entities.Clear();
131 }
132
133 #region Update Methods
134
135 protected internal void UpdatePreparePhysics()
136 {
137 // If we are using a threaded physics engine
138 // grab the latest scene from the engine before
139 // trying to process it.
140
141 // PhysX does this (runs in the background).
142
143 if (_PhyScene.IsThreaded)
144 {
145 _PhyScene.GetResults();
146 }
147 }
148
149 protected internal void UpdateEntities()
150 {
151 List<EntityBase> updateEntities = GetEntities();
152
153 foreach (EntityBase entity in updateEntities)
154 {
155 entity.Update();
156 }
157 }
158
159 protected internal void UpdatePresences()
160 {
161 List<ScenePresence> updateScenePresences = GetScenePresences();
162 foreach (ScenePresence pres in updateScenePresences)
163 {
164 pres.Update();
165 }
166 }
167
168 protected internal float UpdatePhysics(double elapsed)
169 {
170 lock (m_syncRoot)
171 {
172 // Here is where the Scene calls the PhysicsScene. This is a one-way
173 // interaction; the PhysicsScene cannot access the calling Scene directly.
174 // But with joints, we want a PhysicsActor to be able to influence a
175 // non-physics SceneObjectPart. In particular, a PhysicsActor that is connected
176 // with a joint should be able to move the SceneObjectPart which is the visual
177 // representation of that joint (for editing and serialization purposes).
178 // However the PhysicsActor normally cannot directly influence anything outside
179 // of the PhysicsScene, and the non-physical SceneObjectPart which represents
180 // the joint in the Scene does not exist in the PhysicsScene.
181 //
182 // To solve this, we have an event in the PhysicsScene that is fired when a joint
183 // has changed position (because one of its associated PhysicsActors has changed
184 // position).
185 //
186 // Therefore, JointMoved and JointDeactivated events will be fired as a result of the following Simulate().
187
188 return _PhyScene.Simulate((float)elapsed);
189 }
190 }
191
192 protected internal void UpdateEntityMovement()
193 {
194 List<EntityBase> moveEntities = GetEntities();
195
196 foreach (EntityBase entity in moveEntities)
197 {
198 //cfk. This throws occaisional exceptions on a heavily used region
199 //and I added this null check to try to preclude the exception.
200 if (entity != null)
201 entity.UpdateMovement();
202 }
203 }
204
205 #endregion
206
207 #region Entity Methods
208
209 /// <summary>
210 /// Add an object into the scene that has come from storage
211 /// </summary>
212 /// <param name="sceneObject"></param>
213 /// <param name="attachToBackup">
214 /// If true, changes to the object will be reflected in its persisted data
215 /// If false, the persisted data will not be changed even if the object in the scene is changed
216 /// </param>
217 /// <param name="alreadyPersisted">
218 /// If true, we won't persist this object until it changes
219 /// If false, we'll persist this object immediately
220 /// </param>
221 /// <returns>
222 /// true if the object was added, false if an object with the same uuid was already in the scene
223 /// </returns>
224 protected internal bool AddRestoredSceneObject(
225 SceneObjectGroup sceneObject, bool attachToBackup, bool alreadyPersisted)
226 {
227 if (!alreadyPersisted)
228 {
229 sceneObject.ForceInventoryPersistence();
230 sceneObject.HasGroupChanged = true;
231 }
232
233 return AddSceneObject(sceneObject, attachToBackup);
234 }
235
236 /// <summary>
237 /// Add a newly created object to the scene. This will both update the scene, and send information about the
238 /// new object to all clients interested in the scene.
239 /// </summary>
240 /// <param name="sceneObject"></param>
241 /// <param name="attachToBackup">
242 /// If true, the object is made persistent into the scene.
243 /// If false, the object will not persist over server restarts
244 /// </param>
245 /// <returns>
246 /// true if the object was added, false if an object with the same uuid was already in the scene
247 /// </returns>
248 protected internal bool AddNewSceneObject(SceneObjectGroup sceneObject, bool attachToBackup)
249 {
250 // Ensure that we persist this new scene object
251 sceneObject.HasGroupChanged = true;
252
253 return AddSceneObject(sceneObject, attachToBackup);
254 }
255
256 /// <summary>
257 /// Add an object to the scene. This will both update the scene, and send information about the
258 /// new object to all clients interested in the scene.
259 /// </summary>
260 /// <param name="sceneObject"></param>
261 /// <param name="attachToBackup">
262 /// If true, the object is made persistent into the scene.
263 /// If false, the object will not persist over server restarts
264 /// </param>
265 /// <returns>true if the object was added, false if an object with the same uuid was already in the scene
266 /// </returns>
267 protected bool AddSceneObject(SceneObjectGroup sceneObject, bool attachToBackup)
268 {
269 if (sceneObject == null || sceneObject.RootPart == null || sceneObject.RootPart.UUID == UUID.Zero)
270 return false;
271
272 if (m_parentScene.m_clampPrimSize)
273 {
274 foreach (SceneObjectPart part in sceneObject.Children.Values)
275 {
276 Vector3 scale = part.Shape.Scale;
277
278 if (scale.X > m_parentScene.m_maxNonphys)
279 scale.X = m_parentScene.m_maxNonphys;
280 if (scale.Y > m_parentScene.m_maxNonphys)
281 scale.Y = m_parentScene.m_maxNonphys;
282 if (scale.Z > m_parentScene.m_maxNonphys)
283 scale.Z = m_parentScene.m_maxNonphys;
284
285 part.Shape.Scale = scale;
286 }
287 }
288
289 sceneObject.AttachToScene(m_parentScene);
290
291 lock (sceneObject)
292 {
293 if (!Entities.ContainsKey(sceneObject.UUID))
294 {
295 Entities.Add(sceneObject);
296 m_numPrim += sceneObject.Children.Count;
297
298 if (attachToBackup)
299 sceneObject.AttachToBackup();
300
301 if (OnObjectCreate != null)
302 OnObjectCreate(sceneObject);
303
304 return true;
305 }
306 }
307
308 return false;
309 }
310
311 /// <summary>
312 /// Delete an object from the scene
313 /// </summary>
314 /// <returns>true if the object was deleted, false if there was no object to delete</returns>
315 public bool DeleteSceneObject(UUID uuid, bool resultOfObjectLinked)
316 {
317 if (Entities.ContainsKey(uuid))
318 {
319 if (!resultOfObjectLinked)
320 {
321 m_numPrim -= ((SceneObjectGroup) Entities[uuid]).Children.Count;
322
323 if ((((SceneObjectGroup)Entities[uuid]).RootPart.Flags & PrimFlags.Physics) == PrimFlags.Physics)
324 {
325 RemovePhysicalPrim(((SceneObjectGroup)Entities[uuid]).Children.Count);
326 }
327 }
328
329 if (OnObjectRemove != null)
330 OnObjectRemove(Entities[uuid]);
331
332 Entities.Remove(uuid);
333 //SceneObjectGroup part;
334 //((part.RootPart.Flags & PrimFlags.Physics) == PrimFlags.Physics)
335
336 return true;
337 }
338
339 return false;
340 }
341
342 /// <summary>
343 /// Add an entity to the list of prims to process on the next update
344 /// </summary>
345 /// <param name="obj">
346 /// A <see cref="EntityBase"/>
347 /// </param>
348 protected internal void AddToUpdateList(EntityBase obj)
349 {
350 lock (m_updateList)
351 {
352 if (!m_updateList.Contains(obj))
353 {
354 m_updateList.Add(obj);
355 }
356 }
357 }
358
359 /// <summary>
360 /// Process all pending updates
361 /// </summary>
362 protected internal void ProcessUpdates()
363 {
364 lock (m_updateList)
365 {
366 for (int i = 0; i < m_updateList.Count; i++)
367 {
368 EntityBase entity = m_updateList[i];
369
370 // Don't abort the whole update if one entity happens to give us an exception.
371 try
372 {
373 m_updateList[i].Update();
374 }
375 catch (Exception e)
376 {
377 m_log.ErrorFormat(
378 "[INNER SCENE]: Failed to update {0}, {1} - {2}", entity.Name, entity.UUID, e);
379 }
380 }
381
382 m_updateList.Clear();
383 }
384 }
385
386 protected internal void AddPhysicalPrim(int number)
387 {
388 m_physicalPrim++;
389 }
390
391 protected internal void RemovePhysicalPrim(int number)
392 {
393 m_physicalPrim--;
394 }
395
396 protected internal void AddToScriptLPS(int number)
397 {
398 m_scriptLPS += number;
399 }
400
401 protected internal void AddActiveScripts(int number)
402 {
403 m_activeScripts += number;
404 }
405
406 protected internal void DropObject(uint objectLocalID, IClientAPI remoteClient)
407 {
408 List<EntityBase> EntityList = GetEntities();
409
410 foreach (EntityBase obj in EntityList)
411 {
412 if (obj is SceneObjectGroup)
413 {
414 if (((SceneObjectGroup)obj).LocalId == objectLocalID)
415 {
416 SceneObjectGroup group = (SceneObjectGroup)obj;
417
418 m_parentScene.DetachSingleAttachmentToGround(group.UUID,remoteClient);
419 }
420 }
421 }
422 }
423
424 protected internal void DetachObject(uint objectLocalID, IClientAPI remoteClient)
425 {
426 List<EntityBase> EntityList = GetEntities();
427
428 foreach (EntityBase obj in EntityList)
429 {
430 if (obj is SceneObjectGroup)
431 {
432 if (((SceneObjectGroup)obj).LocalId == objectLocalID)
433 {
434 SceneObjectGroup group = (SceneObjectGroup)obj;
435
436 //group.DetachToGround();
437 m_parentScene.DetachSingleAttachmentToInv(group.GetFromAssetID(),remoteClient);
438 }
439 }
440 }
441 }
442
443 protected internal void HandleUndo(IClientAPI remoteClient, UUID primId)
444 {
445 if (primId != UUID.Zero)
446 {
447 SceneObjectPart part = m_parentScene.GetSceneObjectPart(primId);
448 if (part != null)
449 part.Undo();
450 }
451 }
452
453 protected internal void HandleObjectGroupUpdate(
454 IClientAPI remoteClient, UUID GroupID, uint objectLocalID, UUID Garbage)
455 {
456 List<EntityBase> EntityList = GetEntities();
457
458 foreach (EntityBase obj in EntityList)
459 {
460 if (obj is SceneObjectGroup)
461 {
462 if (((SceneObjectGroup)obj).LocalId == objectLocalID)
463 {
464 SceneObjectGroup group = (SceneObjectGroup)obj;
465
466 if (group.OwnerID == remoteClient.AgentId)
467 group.SetGroup(GroupID, remoteClient);
468 }
469 }
470 }
471 }
472
473 /// <summary>
474 /// Event Handling routine for Attach Object
475 /// </summary>
476 /// <param name="remoteClient"></param>
477 /// <param name="objectLocalID"></param>
478 /// <param name="AttachmentPt"></param>
479 /// <param name="rot"></param>
480 protected internal void AttachObject(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, bool silent)
481 {
482 // If we can't take it, we can't attach it!
483 //
484 SceneObjectPart part = m_parentScene.GetSceneObjectPart(objectLocalID);
485 if (part == null)
486 return;
487
488 if (!m_parentScene.Permissions.CanTakeObject(
489 part.UUID, remoteClient.AgentId))
490 return;
491
492 // Calls attach with a Zero position
493 //
494 AttachObject(remoteClient, objectLocalID, AttachmentPt, rot, Vector3.Zero, false);
495 }
496
497 public SceneObjectGroup RezSingleAttachment(
498 IClientAPI remoteClient, UUID itemID, uint AttachmentPt)
499 {
500 SceneObjectGroup objatt = m_parentScene.RezObject(remoteClient,
501 itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
502 false, false, remoteClient.AgentId, true);
503
504
505 if (objatt != null)
506 {
507 bool tainted = false;
508 if (AttachmentPt != 0 && AttachmentPt != objatt.GetAttachmentPoint())
509 tainted = true;
510
511 AttachObject(remoteClient, objatt.LocalId, AttachmentPt, Quaternion.Identity, objatt.AbsolutePosition, false);
512 objatt.ScheduleGroupForFullUpdate();
513 if (tainted)
514 objatt.HasGroupChanged = true;
515
516 // Fire after attach, so we don't get messy perms dialogs
517 //
518 objatt.CreateScriptInstances(0, true, m_parentScene.DefaultScriptEngine, 0);
519 }
520 return objatt;
521 }
522
523 // What makes this method odd and unique is it tries to detach using an UUID.... Yay for standards.
524 // To LocalId or UUID, *THAT* is the question. How now Brown UUID??
525 public void DetachSingleAttachmentToInv(UUID itemID, IClientAPI remoteClient)
526 {
527 if (itemID == UUID.Zero) // If this happened, someone made a mistake....
528 return;
529
530 List<EntityBase> EntityList = GetEntities();
531
532 foreach (EntityBase obj in EntityList)
533 {
534 if (obj is SceneObjectGroup)
535 {
536 if (((SceneObjectGroup)obj).GetFromAssetID() == itemID)
537 {
538 SceneObjectGroup group = (SceneObjectGroup)obj;
539 group.DetachToInventoryPrep();
540 m_log.Debug("[DETACH]: Saving attachpoint: " + ((uint)group.GetAttachmentPoint()).ToString());
541 m_parentScene.updateKnownAsset(remoteClient, group, group.GetFromAssetID(), group.OwnerID);
542 m_parentScene.DeleteSceneObject(group, false);
543 }
544 }
545 }
546 }
547
548 protected internal void AttachObject(
549 IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, Vector3 attachPos, bool silent)
550 {
551 List<EntityBase> EntityList = GetEntities();
552 foreach (EntityBase obj in EntityList)
553 {
554 if (obj is SceneObjectGroup)
555 {
556 if (((SceneObjectGroup)obj).LocalId == objectLocalID)
557 {
558 SceneObjectGroup group = (SceneObjectGroup)obj;
559 if (m_parentScene.Permissions.CanTakeObject(obj.UUID, remoteClient.AgentId))
560 {
561 // If the attachment point isn't the same as the one previously used
562 // set it's offset position = 0 so that it appears on the attachment point
563 // and not in a weird location somewhere unknown.
564 if (AttachmentPt != 0 && AttachmentPt != (uint)group.GetAttachmentPoint())
565 {
566
567 attachPos = Vector3.Zero;
568 }
569
570 // AttachmentPt 0 means the client chose to 'wear' the attachment.
571 if (AttachmentPt == 0)
572 {
573
574 // Check object for stored attachment point
575 AttachmentPt = (uint)group.GetAttachmentPoint();
576
577
578 }
579
580 // if we still didn't find a suitable attachment point.......
581 if (AttachmentPt == 0)
582 {
583 // Stick it on left hand with Zero Offset from the attachment point.
584 AttachmentPt = (uint)AttachmentPoint.LeftHand;
585 attachPos = Vector3.Zero;
586 }
587
588 group.SetAttachmentPoint(Convert.ToByte(AttachmentPt));
589 group.AbsolutePosition = attachPos;
590
591 // Saves and gets assetID
592 UUID itemId;
593 if (group.GetFromAssetID() == UUID.Zero)
594 {
595 m_parentScene.attachObjectAssetStore(remoteClient, group, remoteClient.AgentId, out itemId);
596 }
597 else
598 {
599 itemId = group.GetFromAssetID();
600 }
601
602 m_parentScene.AttachObject(remoteClient, AttachmentPt, itemId, group);
603
604 group.AttachToAgent(remoteClient.AgentId, AttachmentPt, attachPos, silent);
605 // In case it is later dropped again, don't let
606 // it get cleaned up
607 //
608 group.RootPart.RemFlag(PrimFlags.TemporaryOnRez);
609 group.HasGroupChanged = false;
610 }
611 else
612 {
613 remoteClient.SendAgentAlertMessage("You don't have sufficient permissions to attach this object", false);
614 }
615
616 }
617 }
618 }
619 }
620
621 protected internal ScenePresence CreateAndAddChildScenePresence(IClientAPI client, AvatarAppearance appearance)
622 {
623 ScenePresence newAvatar = null;
624
625 newAvatar = new ScenePresence(client, m_parentScene, m_regInfo, appearance);
626 newAvatar.IsChildAgent = true;
627
628 AddScenePresence(newAvatar);
629
630 return newAvatar;
631 }
632
633 /// <summary>
634 /// Add a presence to the scene
635 /// </summary>
636 /// <param name="presence"></param>
637 protected internal void AddScenePresence(ScenePresence presence)
638 {
639 bool child = presence.IsChildAgent;
640
641 if (child)
642 {
643 m_numChildAgents++;
644 }
645 else
646 {
647 m_numRootAgents++;
648 presence.AddToPhysicalScene();
649 }
650
651 Entities[presence.UUID] = presence;
652
653 lock (ScenePresences)
654 {
655 ScenePresences[presence.UUID] = presence;
656 }
657 }
658
659 /// <summary>
660 /// Remove a presence from the scene
661 /// </summary>
662 protected internal void RemoveScenePresence(UUID agentID)
663 {
664 if (!Entities.Remove(agentID))
665 {
666 m_log.WarnFormat(
667 "[SCENE] Tried to remove non-existent scene presence with agent ID {0} from scene Entities list",
668 agentID);
669 }
670
671 lock (ScenePresences)
672 {
673 if (!ScenePresences.Remove(agentID))
674 {
675 m_log.WarnFormat("[SCENE] Tried to remove non-existent scene presence with agent ID {0} from scene ScenePresences list", agentID);
676 }
677// else
678// {
679// m_log.InfoFormat("[SCENE] Removed scene presence {0} from scene presences list", agentID);
680// }
681 }
682 }
683
684 protected internal void SwapRootChildAgent(bool direction_RC_CR_T_F)
685 {
686 if (direction_RC_CR_T_F)
687 {
688 m_numRootAgents--;
689 m_numChildAgents++;
690 }
691 else
692 {
693 m_numChildAgents--;
694 m_numRootAgents++;
695 }
696 }
697
698 protected internal void removeUserCount(bool TypeRCTF)
699 {
700 if (TypeRCTF)
701 {
702 m_numRootAgents--;
703 }
704 else
705 {
706 m_numChildAgents--;
707 }
708 }
709
710 public void RecalculateStats()
711 {
712 List<ScenePresence> SPList = GetScenePresences();
713 int rootcount = 0;
714 int childcount = 0;
715
716 foreach (ScenePresence user in SPList)
717 {
718 if (user.IsChildAgent)
719 childcount++;
720 else
721 rootcount++;
722 }
723 m_numRootAgents = rootcount;
724 m_numChildAgents = childcount;
725
726 }
727
728 public int GetChildAgentCount()
729 {
730 // some network situations come in where child agents get closed twice.
731 if (m_numChildAgents < 0)
732 {
733 m_numChildAgents = 0;
734 }
735
736 return m_numChildAgents;
737 }
738
739 public int GetRootAgentCount()
740 {
741 return m_numRootAgents;
742 }
743
744 public int GetTotalObjectsCount()
745 {
746 return m_numPrim;
747 }
748
749 public int GetActiveObjectsCount()
750 {
751 return m_physicalPrim;
752 }
753
754 public int GetActiveScriptsCount()
755 {
756 return m_activeScripts;
757 }
758
759 public int GetScriptLPS()
760 {
761 int returnval = m_scriptLPS;
762 m_scriptLPS = 0;
763 return returnval;
764 }
765
766 #endregion
767
768 #region Get Methods
769
770 /// <summary>
771 /// Request a List of all scene presences in this scene. This is a new list, so no
772 /// locking is required to iterate over it.
773 /// </summary>
774 /// <returns></returns>
775 protected internal List<ScenePresence> GetScenePresences()
776 {
777 lock (ScenePresences)
778 {
779 return new List<ScenePresence>(ScenePresences.Values);
780 }
781 }
782
783 protected internal List<ScenePresence> GetAvatars()
784 {
785 List<ScenePresence> result =
786 GetScenePresences(delegate(ScenePresence scenePresence) { return !scenePresence.IsChildAgent; });
787
788 return result;
789 }
790
791 /// <summary>
792 /// Get the controlling client for the given avatar, if there is one.
793 ///
794 /// FIXME: The only user of the method right now is Caps.cs, in order to resolve a client API since it can't
795 /// use the ScenePresence. This could be better solved in a number of ways - we could establish an
796 /// OpenSim.Framework.IScenePresence, or move the caps code into a region package (which might be the more
797 /// suitable solution).
798 /// </summary>
799 /// <param name="agentId"></param>
800 /// <returns>null if either the avatar wasn't in the scene, or
801 /// they do not have a controlling client</returns>
802 /// <remarks>this used to be protected internal, but that
803 /// prevents CapabilitiesModule from accessing it</remarks>
804 public IClientAPI GetControllingClient(UUID agentId)
805 {
806 ScenePresence presence = GetScenePresence(agentId);
807
808 if (presence != null)
809 {
810 return presence.ControllingClient;
811 }
812
813 return null;
814 }
815
816 /// <summary>
817 /// Request a filtered list of m_scenePresences in this World
818 /// </summary>
819 /// <returns></returns>
820 protected internal List<ScenePresence> GetScenePresences(FilterAvatarList filter)
821 {
822 // No locking of scene presences here since we're passing back a list...
823
824 List<ScenePresence> result = new List<ScenePresence>();
825 List<ScenePresence> ScenePresencesList = GetScenePresences();
826
827 foreach (ScenePresence avatar in ScenePresencesList)
828 {
829 if (filter(avatar))
830 {
831 result.Add(avatar);
832 }
833 }
834
835 return result;
836 }
837
838 /// <summary>
839 /// Request a scene presence by UUID
840 /// </summary>
841 /// <param name="avatarID"></param>
842 /// <returns>null if the agent was not found</returns>
843 protected internal ScenePresence GetScenePresence(UUID agentID)
844 {
845 ScenePresence sp;
846
847 lock (ScenePresences)
848 {
849 ScenePresences.TryGetValue(agentID, out sp);
850 }
851
852 return sp;
853 }
854
855 /// <summary>
856 /// Get a scene object group that contains the prim with the given local id
857 /// </summary>
858 /// <param name="localID"></param>
859 /// <returns>null if no scene object group containing that prim is found</returns>
860 private SceneObjectGroup GetGroupByPrim(uint localID)
861 {
862 //m_log.DebugFormat("Entered GetGroupByPrim with localID {0}", localID);
863 List<EntityBase> EntityList = GetEntities();
864 foreach (EntityBase ent in EntityList)
865 {
866 //m_log.DebugFormat("Looking at entity {0}", ent.UUID);
867 if (ent is SceneObjectGroup)
868 {
869 if (((SceneObjectGroup)ent).HasChildPrim(localID))
870 return (SceneObjectGroup)ent;
871 }
872 }
873 return null;
874 }
875
876 /// <summary>
877 /// Get a scene object group that contains the prim with the given uuid
878 /// </summary>
879 /// <param name="fullID"></param>
880 /// <returns>null if no scene object group containing that prim is found</returns>
881 private SceneObjectGroup GetGroupByPrim(UUID fullID)
882 {
883 List<EntityBase> EntityList = GetEntities();
884
885 foreach (EntityBase ent in EntityList)
886 {
887 if (ent is SceneObjectGroup)
888 {
889 if (((SceneObjectGroup)ent).HasChildPrim(fullID))
890 return (SceneObjectGroup)ent;
891 }
892 }
893 return null;
894 }
895
896 protected internal EntityIntersection GetClosestIntersectingPrim(Ray hray, bool frontFacesOnly, bool faceCenters)
897 {
898 // Primitive Ray Tracing
899 float closestDistance = 280f;
900 EntityIntersection returnResult = new EntityIntersection();
901 List<EntityBase> EntityList = GetEntities();
902 foreach (EntityBase ent in EntityList)
903 {
904 if (ent is SceneObjectGroup)
905 {
906 SceneObjectGroup reportingG = (SceneObjectGroup)ent;
907 EntityIntersection result = reportingG.TestIntersection(hray, frontFacesOnly, faceCenters);
908 if (result.HitTF)
909 {
910 if (result.distance < closestDistance)
911 {
912 closestDistance = result.distance;
913 returnResult = result;
914 }
915 }
916 }
917 }
918 return returnResult;
919 }
920
921 /// <summary>
922 /// Get a part contained in this scene.
923 /// </summary>
924 /// <param name="localID"></param>
925 /// <returns>null if the part was not found</returns>
926 protected internal SceneObjectPart GetSceneObjectPart(uint localID)
927 {
928 SceneObjectGroup group = GetGroupByPrim(localID);
929
930 if (group != null)
931 return group.GetChildPart(localID);
932 else
933 return null;
934 }
935
936 /// <summary>
937 /// Get a named prim contained in this scene (will return the first
938 /// found, if there are more than one prim with the same name)
939 /// </summary>
940 /// <param name="name"></param>
941 /// <returns>null if the part was not found</returns>
942 protected internal SceneObjectPart GetSceneObjectPart(string name)
943 {
944 List<EntityBase> EntityList = GetEntities();
945
946 // FIXME: use a dictionary here
947 foreach (EntityBase ent in EntityList)
948 {
949 if (ent is SceneObjectGroup)
950 {
951 foreach (SceneObjectPart p in ((SceneObjectGroup) ent).GetParts())
952 {
953 if (p.Name==name)
954 {
955 return p;
956 }
957 }
958 }
959 }
960 return null;
961 }
962
963 /// <summary>
964 /// Get a part contained in this scene.
965 /// </summary>
966 /// <param name="fullID"></param>
967 /// <returns>null if the part was not found</returns>
968 protected internal SceneObjectPart GetSceneObjectPart(UUID fullID)
969 {
970 SceneObjectGroup group = GetGroupByPrim(fullID);
971
972 if (group != null)
973 return group.GetChildPart(fullID);
974 else
975 return null;
976 }
977
978 protected internal bool TryGetAvatar(UUID avatarId, out ScenePresence avatar)
979 {
980 ScenePresence presence;
981
982 lock (ScenePresences)
983 {
984 if (ScenePresences.TryGetValue(avatarId, out presence))
985 {
986 avatar = presence;
987 return true;
988
989 //if (!presence.IsChildAgent)
990 //{
991 // avatar = presence;
992 // return true;
993 //}
994 //else
995 //{
996 // m_log.WarnFormat(
997 // "[INNER SCENE]: Requested avatar {0} could not be found in scene {1} since it is only registered as a child agent!",
998 // avatarId, m_parentScene.RegionInfo.RegionName);
999 //}
1000 }
1001 }
1002
1003 avatar = null;
1004 return false;
1005 }
1006
1007 protected internal bool TryGetAvatarByName(string avatarName, out ScenePresence avatar)
1008 {
1009 lock (ScenePresences)
1010 {
1011 foreach (ScenePresence presence in ScenePresences.Values)
1012 {
1013 if (!presence.IsChildAgent)
1014 {
1015 string name = presence.ControllingClient.Name;
1016
1017 if (String.Compare(avatarName, name, true) == 0)
1018 {
1019 avatar = presence;
1020 return true;
1021 }
1022 }
1023 }
1024 }
1025
1026 avatar = null;
1027 return false;
1028 }
1029
1030 /// <summary>
1031 /// Returns a list of the entities in the scene. This is a new list so no locking is required to iterate over
1032 /// it
1033 /// </summary>
1034 /// <returns></returns>
1035 protected internal List<EntityBase> GetEntities()
1036 {
1037 return Entities.GetEntities();
1038 }
1039
1040 public Dictionary<uint, float> GetTopScripts()
1041 {
1042 Dictionary<uint, float> topScripts = new Dictionary<uint, float>();
1043
1044 List<EntityBase> EntityList = GetEntities();
1045 int limit = 0;
1046 foreach (EntityBase ent in EntityList)
1047 {
1048 if (ent is SceneObjectGroup)
1049 {
1050 SceneObjectGroup grp = (SceneObjectGroup)ent;
1051 if ((grp.RootPart.GetEffectiveObjectFlags() & (uint)PrimFlags.Scripted) != 0)
1052 {
1053 if (grp.scriptScore >= 0.01)
1054 {
1055 topScripts.Add(grp.LocalId, grp.scriptScore);
1056 limit++;
1057 if (limit >= 100)
1058 {
1059 break;
1060 }
1061 }
1062 grp.scriptScore = 0;
1063 }
1064 }
1065 }
1066
1067 return topScripts;
1068 }
1069
1070 #endregion
1071
1072 #region Other Methods
1073
1074 protected internal void physicsBasedCrash()
1075 {
1076 handlerPhysicsCrash = UnRecoverableError;
1077 if (handlerPhysicsCrash != null)
1078 {
1079 handlerPhysicsCrash();
1080 }
1081 }
1082
1083 protected internal UUID ConvertLocalIDToFullID(uint localID)
1084 {
1085 SceneObjectGroup group = GetGroupByPrim(localID);
1086 if (group != null)
1087 return group.GetPartsFullID(localID);
1088 else
1089 return UUID.Zero;
1090 }
1091
1092 protected internal void ForEachClient(Action<IClientAPI> action)
1093 {
1094 List<ScenePresence> splist = GetScenePresences();
1095 foreach (ScenePresence presence in splist)
1096 {
1097 try
1098 {
1099 action(presence.ControllingClient);
1100 }
1101 catch (Exception e)
1102 {
1103 // Catch it and move on. This includes situations where splist has inconsistent info
1104 m_log.WarnFormat("[SCENE]: Problem processing action in ForEachClient: ", e.Message);
1105 }
1106 }
1107 }
1108
1109 #endregion
1110
1111 #region Client Event handlers
1112
1113 /// <summary>
1114 ///
1115 /// </summary>
1116 /// <param name="localID"></param>
1117 /// <param name="scale"></param>
1118 /// <param name="remoteClient"></param>
1119 protected internal void UpdatePrimScale(uint localID, Vector3 scale, IClientAPI remoteClient)
1120 {
1121 SceneObjectGroup group = GetGroupByPrim(localID);
1122 if (group != null)
1123 {
1124 if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId))
1125 {
1126 group.Resize(scale, localID);
1127 }
1128 }
1129 }
1130
1131 protected internal void UpdatePrimGroupScale(uint localID, Vector3 scale, IClientAPI remoteClient)
1132 {
1133 SceneObjectGroup group = GetGroupByPrim(localID);
1134 if (group != null)
1135 {
1136 if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId))
1137 {
1138 group.GroupResize(scale, localID);
1139 }
1140 }
1141 }
1142
1143 /// <summary>
1144 /// This handles the nifty little tool tip that you get when you drag your mouse over an object
1145 /// Send to the Object Group to process. We don't know enough to service the request
1146 /// </summary>
1147 /// <param name="remoteClient"></param>
1148 /// <param name="AgentID"></param>
1149 /// <param name="RequestFlags"></param>
1150 /// <param name="ObjectID"></param>
1151 protected internal void RequestObjectPropertiesFamily(
1152 IClientAPI remoteClient, UUID AgentID, uint RequestFlags, UUID ObjectID)
1153 {
1154 SceneObjectGroup group = GetGroupByPrim(ObjectID);
1155 if (group != null)
1156 {
1157 group.ServiceObjectPropertiesFamilyRequest(remoteClient, AgentID, RequestFlags);
1158 }
1159 }
1160
1161 /// <summary>
1162 ///
1163 /// </summary>
1164 /// <param name="localID"></param>
1165 /// <param name="rot"></param>
1166 /// <param name="remoteClient"></param>
1167 protected internal void UpdatePrimSingleRotation(uint localID, Quaternion rot, IClientAPI remoteClient)
1168 {
1169 SceneObjectGroup group = GetGroupByPrim(localID);
1170 if (group != null)
1171 {
1172 if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId))
1173 {
1174 group.UpdateSingleRotation(rot, localID);
1175 }
1176 }
1177 }
1178
1179 /// <summary>
1180 ///
1181 /// </summary>
1182 /// <param name="localID"></param>
1183 /// <param name="rot"></param>
1184 /// <param name="remoteClient"></param>
1185 protected internal void UpdatePrimRotation(uint localID, Quaternion rot, IClientAPI remoteClient)
1186 {
1187 SceneObjectGroup group = GetGroupByPrim(localID);
1188 if (group != null)
1189 {
1190 if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId))
1191 {
1192 group.UpdateGroupRotation(rot);
1193 }
1194 }
1195 }
1196
1197 /// <summary>
1198 ///
1199 /// </summary>
1200 /// <param name="localID"></param>
1201 /// <param name="pos"></param>
1202 /// <param name="rot"></param>
1203 /// <param name="remoteClient"></param>
1204 protected internal void UpdatePrimRotation(uint localID, Vector3 pos, Quaternion rot, IClientAPI remoteClient)
1205 {
1206 SceneObjectGroup group = GetGroupByPrim(localID);
1207 if (group != null)
1208 {
1209 if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId))
1210 {
1211 group.UpdateGroupRotation(pos, rot);
1212 }
1213 }
1214 }
1215
1216 /// <summary>
1217 /// Update the position of the given part
1218 /// </summary>
1219 /// <param name="localID"></param>
1220 /// <param name="pos"></param>
1221 /// <param name="remoteClient"></param>
1222 protected internal void UpdatePrimSinglePosition(uint localID, Vector3 pos, IClientAPI remoteClient)
1223 {
1224 SceneObjectGroup group = GetGroupByPrim(localID);
1225 if (group != null)
1226 {
1227 if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId) || group.IsAttachment)
1228 {
1229 group.UpdateSinglePosition(pos, localID);
1230 }
1231 }
1232 }
1233
1234 /// <summary>
1235 /// Update the position of the given part
1236 /// </summary>
1237 /// <param name="localID"></param>
1238 /// <param name="pos"></param>
1239 /// <param name="remoteClient"></param>
1240 protected internal void UpdatePrimPosition(uint localID, Vector3 pos, IClientAPI remoteClient)
1241 {
1242 SceneObjectGroup group = GetGroupByPrim(localID);
1243 if (group != null)
1244 {
1245
1246 // Vector3 oldPos = group.AbsolutePosition;
1247 if (group.IsAttachment || (group.RootPart.Shape.PCode == 9 && group.RootPart.Shape.State != 0))
1248 {
1249 group.UpdateGroupPosition(pos);
1250 }
1251 else
1252 {
1253 if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId) && m_parentScene.Permissions.CanObjectEntry(group.UUID, false, pos))
1254 {
1255 group.UpdateGroupPosition(pos);
1256 }
1257 }
1258 }
1259 }
1260
1261 /// <summary>
1262 ///
1263 /// </summary>
1264 /// <param name="localID"></param>
1265 /// <param name="texture"></param>
1266 /// <param name="remoteClient"></param>
1267 protected internal void UpdatePrimTexture(uint localID, byte[] texture, IClientAPI remoteClient)
1268 {
1269 SceneObjectGroup group = GetGroupByPrim(localID);
1270 if (group != null)
1271 {
1272 if (m_parentScene.Permissions.CanEditObject(group.UUID,remoteClient.AgentId))
1273 {
1274 group.UpdateTextureEntry(localID, texture);
1275 }
1276 }
1277 }
1278
1279 /// <summary>
1280 ///
1281 /// </summary>
1282 /// <param name="localID"></param>
1283 /// <param name="packet"></param>
1284 /// <param name="remoteClient"></param>
1285 /// This routine seems to get called when a user changes object settings in the viewer.
1286 /// If some one can confirm that, please change the comment according.
1287 protected internal void UpdatePrimFlags(uint localID, bool UsePhysics, bool IsTemporary, bool IsPhantom, IClientAPI remoteClient)
1288 {
1289 SceneObjectGroup group = GetGroupByPrim(localID);
1290 if (group != null)
1291 {
1292 if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId))
1293 {
1294 group.UpdatePrimFlags(localID, UsePhysics, IsTemporary, IsPhantom, false); // VolumeDetect can't be set via UI and will always be off when a change is made there
1295 }
1296 }
1297 }
1298
1299 /// <summary>
1300 /// Move the given object
1301 /// </summary>
1302 /// <param name="objectID"></param>
1303 /// <param name="offset"></param>
1304 /// <param name="pos"></param>
1305 /// <param name="remoteClient"></param>
1306 protected internal void MoveObject(UUID objectID, Vector3 offset, Vector3 pos, IClientAPI remoteClient, List<SurfaceTouchEventArgs> surfaceArgs)
1307 {
1308 SceneObjectGroup group = GetGroupByPrim(objectID);
1309 if (group != null)
1310 {
1311 if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId))// && PermissionsMngr.)
1312 {
1313 group.GrabMovement(offset, pos, remoteClient);
1314 }
1315 // This is outside the above permissions condition
1316 // so that if the object is locked the client moving the object
1317 // get's it's position on the simulator even if it was the same as before
1318 // This keeps the moving user's client in sync with the rest of the world.
1319 group.SendGroupTerseUpdate();
1320 }
1321 }
1322
1323 /// <summary>
1324 ///
1325 /// </summary>
1326 /// <param name="primLocalID"></param>
1327 /// <param name="description"></param>
1328 protected internal void PrimName(IClientAPI remoteClient, uint primLocalID, string name)
1329 {
1330 SceneObjectGroup group = GetGroupByPrim(primLocalID);
1331 if (group != null)
1332 {
1333 if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId))
1334 {
1335 group.SetPartName(Util.CleanString(name), primLocalID);
1336 group.HasGroupChanged = true;
1337 }
1338 }
1339 }
1340
1341 /// <summary>
1342 ///
1343 /// </summary>
1344 /// <param name="primLocalID"></param>
1345 /// <param name="description"></param>
1346 protected internal void PrimDescription(IClientAPI remoteClient, uint primLocalID, string description)
1347 {
1348 SceneObjectGroup group = GetGroupByPrim(primLocalID);
1349 if (group != null)
1350 {
1351 if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId))
1352 {
1353 group.SetPartDescription(Util.CleanString(description), primLocalID);
1354 group.HasGroupChanged = true;
1355 }
1356 }
1357 }
1358
1359 protected internal void PrimClickAction(IClientAPI remoteClient, uint primLocalID, string clickAction)
1360 {
1361 SceneObjectGroup group = GetGroupByPrim(primLocalID);
1362 if (group != null)
1363 {
1364 if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId))
1365 {
1366 SceneObjectPart part = m_parentScene.GetSceneObjectPart(primLocalID);
1367 part.ClickAction = Convert.ToByte(clickAction);
1368 group.HasGroupChanged = true;
1369 }
1370 }
1371 }
1372
1373 protected internal void PrimMaterial(IClientAPI remoteClient, uint primLocalID, string material)
1374 {
1375 SceneObjectGroup group = GetGroupByPrim(primLocalID);
1376 if (group != null)
1377 {
1378 if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId))
1379 {
1380 SceneObjectPart part = m_parentScene.GetSceneObjectPart(primLocalID);
1381 part.Material = Convert.ToByte(material);
1382 group.HasGroupChanged = true;
1383 }
1384 }
1385 }
1386
1387 protected internal void UpdateExtraParam(UUID agentID, uint primLocalID, ushort type, bool inUse, byte[] data)
1388 {
1389 SceneObjectGroup group = GetGroupByPrim(primLocalID);
1390
1391 if (group != null)
1392 {
1393 if (m_parentScene.Permissions.CanEditObject(group.UUID,agentID))
1394 {
1395 group.UpdateExtraParam(primLocalID, type, inUse, data);
1396 }
1397 }
1398 }
1399
1400 /// <summary>
1401 ///
1402 /// </summary>
1403 /// <param name="primLocalID"></param>
1404 /// <param name="shapeBlock"></param>
1405 protected internal void UpdatePrimShape(UUID agentID, uint primLocalID, UpdateShapeArgs shapeBlock)
1406 {
1407 SceneObjectGroup group = GetGroupByPrim(primLocalID);
1408 if (group != null)
1409 {
1410 if (m_parentScene.Permissions.CanEditObject(group.GetPartsFullID(primLocalID), agentID))
1411 {
1412 ObjectShapePacket.ObjectDataBlock shapeData = new ObjectShapePacket.ObjectDataBlock();
1413 shapeData.ObjectLocalID = shapeBlock.ObjectLocalID;
1414 shapeData.PathBegin = shapeBlock.PathBegin;
1415 shapeData.PathCurve = shapeBlock.PathCurve;
1416 shapeData.PathEnd = shapeBlock.PathEnd;
1417 shapeData.PathRadiusOffset = shapeBlock.PathRadiusOffset;
1418 shapeData.PathRevolutions = shapeBlock.PathRevolutions;
1419 shapeData.PathScaleX = shapeBlock.PathScaleX;
1420 shapeData.PathScaleY = shapeBlock.PathScaleY;
1421 shapeData.PathShearX = shapeBlock.PathShearX;
1422 shapeData.PathShearY = shapeBlock.PathShearY;
1423 shapeData.PathSkew = shapeBlock.PathSkew;
1424 shapeData.PathTaperX = shapeBlock.PathTaperX;
1425 shapeData.PathTaperY = shapeBlock.PathTaperY;
1426 shapeData.PathTwist = shapeBlock.PathTwist;
1427 shapeData.PathTwistBegin = shapeBlock.PathTwistBegin;
1428 shapeData.ProfileBegin = shapeBlock.ProfileBegin;
1429 shapeData.ProfileCurve = shapeBlock.ProfileCurve;
1430 shapeData.ProfileEnd = shapeBlock.ProfileEnd;
1431 shapeData.ProfileHollow = shapeBlock.ProfileHollow;
1432
1433 group.UpdateShape(shapeData, primLocalID);
1434 }
1435 }
1436 }
1437
1438 /// <summary>
1439 /// Initial method invoked when we receive a link objects request from the client.
1440 /// </summary>
1441 /// <param name="client"></param>
1442 /// <param name="parentPrim"></param>
1443 /// <param name="childPrims"></param>
1444 protected internal void LinkObjects(IClientAPI client, uint parentPrim, List<uint> childPrims)
1445 {
1446 List<EntityBase> EntityList = GetEntities();
1447
1448 SceneObjectGroup parenPrim = null;
1449 foreach (EntityBase ent in EntityList)
1450 {
1451 if (ent is SceneObjectGroup)
1452 {
1453 if (((SceneObjectGroup)ent).LocalId == parentPrim)
1454 {
1455 parenPrim = (SceneObjectGroup)ent;
1456 break;
1457 }
1458 }
1459 }
1460
1461 List<SceneObjectGroup> children = new List<SceneObjectGroup>();
1462 if (parenPrim != null)
1463 {
1464 // We do this in reverse to get the link order of the prims correct
1465 for (int i = childPrims.Count - 1; i >= 0; i--)
1466 {
1467 foreach (EntityBase ent in EntityList)
1468 {
1469 if (ent is SceneObjectGroup)
1470 {
1471 if (((SceneObjectGroup)ent).LocalId == childPrims[i])
1472 {
1473 // Make sure no child prim is set for sale
1474 // So that, on delink, no prims are unwittingly
1475 // left for sale and sold off
1476 ((SceneObjectGroup)ent).RootPart.ObjectSaleType = 0;
1477 ((SceneObjectGroup)ent).RootPart.SalePrice = 10;
1478 children.Add((SceneObjectGroup)ent);
1479 }
1480 }
1481 }
1482 }
1483 }
1484 else
1485 {
1486 return; // parent is null so not in this region
1487 }
1488
1489 foreach (SceneObjectGroup sceneObj in children)
1490 {
1491 parenPrim.LinkToGroup(sceneObj);
1492
1493 // this is here so physics gets updated!
1494 // Don't remove! Bad juju! Stay away! or fix physics!
1495 sceneObj.AbsolutePosition = sceneObj.AbsolutePosition;
1496 }
1497
1498 // We need to explicitly resend the newly link prim's object properties since no other actions
1499 // occur on link to invoke this elsewhere (such as object selection)
1500 parenPrim.RootPart.AddFlag(PrimFlags.CreateSelected);
1501 parenPrim.TriggerScriptChangedEvent(Changed.LINK);
1502
1503 if (client != null)
1504 {
1505 parenPrim.GetProperties(client);
1506 }
1507 else
1508 {
1509 foreach (ScenePresence p in GetScenePresences())
1510 {
1511 parenPrim.GetProperties(p.ControllingClient);
1512 }
1513 }
1514 }
1515
1516 /// <summary>
1517 /// Delink a linkset
1518 /// </summary>
1519 /// <param name="prims"></param>
1520 protected internal void DelinkObjects(List<uint> primIds)
1521 {
1522 DelinkObjects(primIds, true);
1523 }
1524
1525 protected internal void DelinkObjects(List<uint> primIds, bool sendEvents)
1526 {
1527 SceneObjectGroup parenPrim = null;
1528
1529 // Need a list of the SceneObjectGroup local ids
1530 // XXX I'm anticipating that building this dictionary once is more efficient than
1531 // repeated scanning of the Entity.Values for a large number of primIds. However, it might
1532 // be more efficient yet to keep this dictionary permanently on hand.
1533
1534 Dictionary<uint, SceneObjectGroup> sceneObjects = new Dictionary<uint, SceneObjectGroup>();
1535
1536 List<EntityBase> EntityList = GetEntities();
1537 foreach (EntityBase ent in EntityList)
1538 {
1539 if (ent is SceneObjectGroup)
1540 {
1541 SceneObjectGroup obj = (SceneObjectGroup)ent;
1542 // Nasty one. Can't unlink anything in the sim
1543 // If a duplicate local ID sneaks in
1544 // So, check it here!
1545 //
1546 if (!sceneObjects.ContainsKey(obj.LocalId))
1547 sceneObjects.Add(obj.LocalId, obj);
1548
1549 }
1550 }
1551
1552 // Find the root prim among the prim ids we've been given
1553 for (int i = 0; i < primIds.Count; i++)
1554 {
1555
1556 if (sceneObjects.ContainsKey(primIds[i]))
1557 {
1558 parenPrim = sceneObjects[primIds[i]];
1559 primIds.RemoveAt(i);
1560 break;
1561 }
1562 }
1563
1564 if (parenPrim != null)
1565 {
1566 foreach (uint childPrimId in primIds)
1567 {
1568 parenPrim.DelinkFromGroup(childPrimId, sendEvents);
1569 }
1570
1571 if (parenPrim.Children.Count == 1)
1572 {
1573 // The link set has been completely torn down
1574 // This is the case if you select a link set and delink
1575 //
1576 parenPrim.RootPart.LinkNum = 0;
1577 if (sendEvents)
1578 parenPrim.TriggerScriptChangedEvent(Changed.LINK);
1579 }
1580 else
1581 {
1582 // The link set has prims remaining. This path is taken
1583 // when a subset of a link set's prims are selected
1584 // and the root prim is part of that selection
1585 //
1586 List<SceneObjectPart> parts = new List<SceneObjectPart>(parenPrim.Children.Values);
1587
1588 List<uint> unlink_ids = new List<uint>();
1589 foreach (SceneObjectPart unlink_part in parts)
1590 unlink_ids.Add(unlink_part.LocalId);
1591
1592 // Tear down the remaining link set
1593 //
1594 if (unlink_ids.Count == 2)
1595 {
1596 DelinkObjects(unlink_ids, true);
1597 return;
1598 }
1599
1600 DelinkObjects(unlink_ids, false);
1601
1602 // Send event to root prim, then we're done with it
1603 parenPrim.TriggerScriptChangedEvent(Changed.LINK);
1604
1605 unlink_ids.Remove(parenPrim.RootPart.LocalId);
1606
1607 foreach (uint localId in unlink_ids)
1608 {
1609 SceneObjectPart nr = GetSceneObjectPart(localId);
1610 nr.UpdateFlag = 0;
1611 }
1612
1613 uint newRoot = unlink_ids[0];
1614 unlink_ids.Remove(newRoot);
1615
1616 LinkObjects(null, newRoot, unlink_ids);
1617 }
1618 }
1619 else
1620 {
1621 // The selected prims were all child prims. Edit linked parts
1622 // without the root prim selected will get us here
1623 //
1624 List<SceneObjectGroup> parents = new List<SceneObjectGroup>();
1625
1626 // If the first scan failed, we need to do a /deep/ scan of the linkages. This is /really/ slow
1627 // We know that this is not the root prim now essentially, so we don't have to worry about remapping
1628 // which one is the root prim
1629 bool delinkedSomething = false;
1630 for (int i = 0; i < primIds.Count; i++)
1631 {
1632 foreach (SceneObjectGroup grp in sceneObjects.Values)
1633 {
1634 SceneObjectPart gPart = grp.GetChildPart(primIds[i]);
1635 if (gPart != null)
1636 {
1637 grp.DelinkFromGroup(primIds[i]);
1638 delinkedSomething = true;
1639 if (!parents.Contains(grp))
1640 parents.Add(grp);
1641 }
1642
1643 }
1644 }
1645 if (!delinkedSomething)
1646 {
1647 m_log.InfoFormat("[SCENE]: " +
1648 "DelinkObjects(): Could not find a root prim out of {0} as given to a delink request!",
1649 primIds);
1650 }
1651 else
1652 {
1653 foreach (SceneObjectGroup g in parents)
1654 {
1655 g.TriggerScriptChangedEvent(Changed.LINK);
1656 }
1657 }
1658 }
1659 }
1660
1661 protected internal void MakeObjectSearchable(IClientAPI remoteClient, bool IncludeInSearch, uint localID)
1662 {
1663 UUID user = remoteClient.AgentId;
1664 UUID objid = UUID.Zero;
1665 SceneObjectPart obj = null;
1666
1667 List<EntityBase> EntityList = GetEntities();
1668 foreach (EntityBase ent in EntityList)
1669 {
1670 if (ent is SceneObjectGroup)
1671 {
1672 foreach (KeyValuePair<UUID, SceneObjectPart> subent in ((SceneObjectGroup)ent).Children)
1673 {
1674 if (subent.Value.LocalId == localID)
1675 {
1676 objid = subent.Key;
1677 obj = subent.Value;
1678 }
1679 }
1680 }
1681 }
1682
1683 //Protip: In my day, we didn't call them searchable objects, we called them limited point-to-point joints
1684 //aka ObjectFlags.JointWheel = IncludeInSearch
1685
1686 //Permissions model: Object can be REMOVED from search IFF:
1687 // * User owns object
1688 //use CanEditObject
1689
1690 //Object can be ADDED to search IFF:
1691 // * User owns object
1692 // * Asset/DRM permission bit "modify" is enabled
1693 //use CanEditObjectPosition
1694
1695 // libomv will complain about PrimFlags.JointWheel being
1696 // deprecated, so we
1697 #pragma warning disable 0612
1698 if (IncludeInSearch && m_parentScene.Permissions.CanEditObject(objid, user))
1699 {
1700 obj.ParentGroup.RootPart.AddFlag(PrimFlags.JointWheel);
1701 obj.ParentGroup.HasGroupChanged = true;
1702 }
1703 else if (!IncludeInSearch && m_parentScene.Permissions.CanMoveObject(objid,user))
1704 {
1705 obj.ParentGroup.RootPart.RemFlag(PrimFlags.JointWheel);
1706 obj.ParentGroup.HasGroupChanged = true;
1707 }
1708 #pragma warning restore 0612
1709 }
1710
1711 /// <summary>
1712 /// Duplicate the given object, Fire and Forget, No rotation, no return wrapper
1713 /// </summary>
1714 /// <param name="originalPrim"></param>
1715 /// <param name="offset"></param>
1716 /// <param name="flags"></param>
1717 protected internal void DuplicateObject(uint originalPrim, Vector3 offset, uint flags, UUID AgentID, UUID GroupID)
1718 {
1719 //m_log.DebugFormat("[SCENE]: Duplication of object {0} at offset {1} requested by agent {2}", originalPrim, offset, AgentID);
1720
1721 // SceneObjectGroup dupe = DuplicateObject(originalPrim, offset, flags, AgentID, GroupID, Quaternion.Zero);
1722 DuplicateObject(originalPrim, offset, flags, AgentID, GroupID, Quaternion.Identity);
1723 }
1724
1725 /// <summary>
1726 /// Duplicate the given object.
1727 /// </summary>
1728 /// <param name="originalPrim"></param>
1729 /// <param name="offset"></param>
1730 /// <param name="flags"></param>
1731 protected internal SceneObjectGroup DuplicateObject(uint originalPrim, Vector3 offset, uint flags, UUID AgentID, UUID GroupID, Quaternion rot)
1732 {
1733 //m_log.DebugFormat("[SCENE]: Duplication of object {0} at offset {1} requested by agent {2}", originalPrim, offset, AgentID);
1734
1735 List<EntityBase> EntityList = GetEntities();
1736
1737 SceneObjectGroup originPrim = null;
1738 foreach (EntityBase ent in EntityList)
1739 {
1740 if (ent is SceneObjectGroup)
1741 {
1742 if (((SceneObjectGroup)ent).LocalId == originalPrim)
1743 {
1744 originPrim = (SceneObjectGroup)ent;
1745 break;
1746 }
1747 }
1748 }
1749
1750 if (originPrim != null)
1751 {
1752 if (m_parentScene.Permissions.CanDuplicateObject(originPrim.Children.Count, originPrim.UUID, AgentID, originPrim.AbsolutePosition))
1753 {
1754 SceneObjectGroup copy = originPrim.Copy(AgentID, GroupID, true);
1755 copy.AbsolutePosition = copy.AbsolutePosition + offset;
1756
1757 Entities.Add(copy);
1758
1759 // Since we copy from a source group that is in selected
1760 // state, but the copy is shown deselected in the viewer,
1761 // We need to clear the selection flag here, else that
1762 // prim never gets persisted at all. The client doesn't
1763 // think it's selected, so it will never send a deselect...
1764 copy.IsSelected = false;
1765
1766 m_numPrim += copy.Children.Count;
1767
1768 if (rot != Quaternion.Identity)
1769 {
1770 copy.UpdateGroupRotation(rot);
1771 }
1772
1773 copy.CreateScriptInstances(0, false, m_parentScene.DefaultScriptEngine, 0);
1774 copy.HasGroupChanged = true;
1775 copy.ScheduleGroupForFullUpdate();
1776
1777 // required for physics to update it's position
1778 copy.AbsolutePosition = copy.AbsolutePosition;
1779
1780 if (OnObjectDuplicate != null)
1781 OnObjectDuplicate(originPrim, copy);
1782
1783 return copy;
1784 }
1785 }
1786 else
1787 {
1788 m_log.WarnFormat("[SCENE]: Attempted to duplicate nonexistant prim id {0}", GroupID);
1789 }
1790
1791 return null;
1792 }
1793
1794 /// <summary>
1795 /// Calculates the distance between two Vector3s
1796 /// </summary>
1797 /// <param name="v1"></param>
1798 /// <param name="v2"></param>
1799 /// <returns></returns>
1800 protected internal float Vector3Distance(Vector3 v1, Vector3 v2)
1801 {
1802 // We don't really need the double floating point precision...
1803 // so casting it to a single
1804
1805 return
1806 (float)
1807 Math.Sqrt((v1.X - v2.X) * (v1.X - v2.X) + (v1.Y - v2.Y) * (v1.Y - v2.Y) + (v1.Z - v2.Z) * (v1.Z - v2.Z));
1808 }
1809
1810 #endregion
1811
1812
1813 }
1814}