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