OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
SensorRepeat.cs
Go to the documentation of this file.
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 OpenSimulator 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 
28 using System;
29 using System.Reflection;
30 using System.Collections.Generic;
31 using OpenMetaverse;
32 using OpenSim.Framework;
33 using log4net;
34 using OpenSim.Region.Framework.Interfaces;
35 using OpenSim.Region.Framework.Scenes;
36 using OpenSim.Region.ScriptEngine.Shared;
37 using OpenSim.Region.ScriptEngine.Shared.Api;
38 
39 namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
40 {
41  public class SensorRepeat
42  {
43 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
44 
48  public class SensorInfo
49  {
50  public uint localID;
51  public UUID itemID;
52  public double interval;
53  public DateTime next;
54 
55  public string name;
56  public UUID keyID;
57  public int type;
58  public double range;
59  public double arc;
61 
62  public SensorInfo Clone()
63  {
64  return (SensorInfo)this.MemberwiseClone();
65  }
66  }
67 
69 
73  public int SensorsCount
74  {
75  get
76  {
77  return SenseRepeaters.Count;
78  }
79  }
80 
81  public SensorRepeat(AsyncCommandManager CmdManager)
82  {
83  m_CmdManager = CmdManager;
84  maximumRange = CmdManager.m_ScriptEngine.Config.GetDouble("SensorMaxRange", 96.0d);
85  maximumToReturn = CmdManager.m_ScriptEngine.Config.GetInt("SensorMaxResults", 16);
86  m_npcModule = m_CmdManager.m_ScriptEngine.World.RequestModuleInterface<INPCModule>();
87  }
88 
89  private INPCModule m_npcModule;
90 
91  private Object SenseLock = new Object();
92 
93  private const int AGENT = 1;
94  private const int AGENT_BY_USERNAME = 0x10;
95  private const int NPC = 0x20;
96  private const int ACTIVE = 2;
97  private const int PASSIVE = 4;
98  private const int SCRIPTED = 8;
99 
100  private double maximumRange = 96.0;
101  private int maximumToReturn = 16;
102 
103  //
104  // Sensed entity
105  //
106  private class SensedEntity : IComparable
107  {
108  public SensedEntity(double detectedDistance, UUID detectedID)
109  {
110  distance = detectedDistance;
111  itemID = detectedID;
112  }
113  public int CompareTo(object obj)
114  {
115  if (!(obj is SensedEntity)) throw new InvalidOperationException();
116  SensedEntity ent = (SensedEntity)obj;
117  if (ent == null || ent.distance < distance) return 1;
118  if (ent.distance > distance) return -1;
119  return 0;
120  }
121  public UUID itemID;
122  public double distance;
123  }
124 
135  private List<SensorInfo> SenseRepeaters = new List<SensorInfo>();
136  private object SenseRepeatListLock = new object();
137 
138  public void SetSenseRepeatEvent(uint m_localID, UUID m_itemID,
139  string name, UUID keyID, int type, double range,
140  double arc, double sec, SceneObjectPart host)
141  {
142  // Always remove first, in case this is a re-set
143  UnSetSenseRepeaterEvents(m_localID, m_itemID);
144 
145  if (sec == 0) // Disabling timer
146  return;
147 
148  // Add to timer
149  SensorInfo ts = new SensorInfo();
150  ts.localID = m_localID;
151  ts.itemID = m_itemID;
152  ts.interval = sec;
153  ts.name = name;
154  ts.keyID = keyID;
155  ts.type = type;
156  if (range > maximumRange)
157  ts.range = maximumRange;
158  else
159  ts.range = range;
160  ts.arc = arc;
161  ts.host = host;
162 
163  ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
164 
165  AddSenseRepeater(ts);
166  }
167 
168  private void AddSenseRepeater(SensorInfo senseRepeater)
169  {
170  lock (SenseRepeatListLock)
171  {
172  List<SensorInfo> newSenseRepeaters = new List<SensorInfo>(SenseRepeaters);
173  newSenseRepeaters.Add(senseRepeater);
174  SenseRepeaters = newSenseRepeaters;
175  }
176  }
177 
178  public void UnSetSenseRepeaterEvents(uint m_localID, UUID m_itemID)
179  {
180  // Remove from timer
181  lock (SenseRepeatListLock)
182  {
183  List<SensorInfo> newSenseRepeaters = new List<SensorInfo>();
184  foreach (SensorInfo ts in SenseRepeaters)
185  {
186  if (ts.localID != m_localID || ts.itemID != m_itemID)
187  {
188  newSenseRepeaters.Add(ts);
189  }
190  }
191 
192  SenseRepeaters = newSenseRepeaters;
193  }
194  }
195 
197  {
198  // Go through all timers
199  foreach (SensorInfo ts in SenseRepeaters)
200  {
201  // Time has passed?
202  if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime())
203  {
204  SensorSweep(ts);
205  // set next interval
206  ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
207  }
208  }
209  }
210 
211  public void SenseOnce(uint m_localID, UUID m_itemID,
212  string name, UUID keyID, int type,
213  double range, double arc, SceneObjectPart host)
214  {
215  // Add to timer
216  SensorInfo ts = new SensorInfo();
217  ts.localID = m_localID;
218  ts.itemID = m_itemID;
219  ts.interval = 0;
220  ts.name = name;
221  ts.keyID = keyID;
222  ts.type = type;
223  if (range > maximumRange)
224  ts.range = maximumRange;
225  else
226  ts.range = range;
227  ts.arc = arc;
228  ts.host = host;
229  SensorSweep(ts);
230  }
231 
232  private void SensorSweep(SensorInfo ts)
233  {
234  if (ts.host == null)
235  {
236  return;
237  }
238 
239  List<SensedEntity> sensedEntities = new List<SensedEntity>();
240 
241  // Is the sensor type is AGENT and not SCRIPTED then include agents
242  if ((ts.type & (AGENT | AGENT_BY_USERNAME | NPC)) != 0 && (ts.type & SCRIPTED) == 0)
243  {
244  sensedEntities.AddRange(doAgentSensor(ts));
245  }
246 
247  // If SCRIPTED or PASSIVE or ACTIVE check objects
248  if ((ts.type & SCRIPTED) != 0 || (ts.type & PASSIVE) != 0 || (ts.type & ACTIVE) != 0)
249  {
250  sensedEntities.AddRange(doObjectSensor(ts));
251  }
252 
253  lock (SenseLock)
254  {
255  if (sensedEntities.Count == 0)
256  {
257  // send a "no_sensor"
258  // Add it to queue
259  m_CmdManager.m_ScriptEngine.PostScriptEvent(ts.itemID,
260  new EventParams("no_sensor", new Object[0],
261  new DetectParams[0]));
262  }
263  else
264  {
265  // Sort the list to get everything ordered by distance
266  sensedEntities.Sort();
267  int count = sensedEntities.Count;
268  int idx;
269  List<DetectParams> detected = new List<DetectParams>();
270  for (idx = 0; idx < count; idx++)
271  {
272  try
273  {
274  DetectParams detect = new DetectParams();
275  detect.Key = sensedEntities[idx].itemID;
276  detect.Populate(m_CmdManager.m_ScriptEngine.World);
277  detected.Add(detect);
278  }
279  catch (Exception)
280  {
281  // Ignore errors, the object has been deleted or the avatar has gone and
282  // there was a problem in detect.Populate so nothing added to the list
283  }
284  if (detected.Count == maximumToReturn)
285  break;
286  }
287 
288  if (detected.Count == 0)
289  {
290  // To get here with zero in the list there must have been some sort of problem
291  // like the object being deleted or the avatar leaving to have caused some
292  // difficulty during the Populate above so fire a no_sensor event
293  m_CmdManager.m_ScriptEngine.PostScriptEvent(ts.itemID,
294  new EventParams("no_sensor", new Object[0],
295  new DetectParams[0]));
296  }
297  else
298  {
299  m_CmdManager.m_ScriptEngine.PostScriptEvent(ts.itemID,
300  new EventParams("sensor",
301  new Object[] {new LSL_Types.LSLInteger(detected.Count) },
302  detected.ToArray()));
303  }
304  }
305  }
306  }
307 
308  private List<SensedEntity> doObjectSensor(SensorInfo ts)
309  {
310  List<EntityBase> Entities;
311  List<SensedEntity> sensedEntities = new List<SensedEntity>();
312 
313  // If this is an object sense by key try to get it directly
314  // rather than getting a list to scan through
315  if (ts.keyID != UUID.Zero)
316  {
317  EntityBase e = null;
318  m_CmdManager.m_ScriptEngine.World.Entities.TryGetValue(ts.keyID, out e);
319  if (e == null)
320  return sensedEntities;
321  Entities = new List<EntityBase>();
322  Entities.Add(e);
323  }
324  else
325  {
326  Entities = new List<EntityBase>(m_CmdManager.m_ScriptEngine.World.GetEntities());
327  }
328  SceneObjectPart SensePoint = ts.host;
329 
330  Vector3 fromRegionPos = SensePoint.GetWorldPosition();
331 
332  // pre define some things to avoid repeated definitions in the loop body
333  Vector3 toRegionPos;
334  double dis;
335  int objtype;
336  SceneObjectPart part;
337  float dx;
338  float dy;
339  float dz;
340 
341 // Quaternion q = SensePoint.RotationOffset;
342  Quaternion q = SensePoint.GetWorldRotation(); // non-attached prim Sensor *always* uses World rotation!
343  if (SensePoint.ParentGroup.IsAttachment)
344  {
345  // In attachments, rotate the sensor cone with the
346  // avatar rotation. This may include a nonzero elevation if
347  // in mouselook.
348  // This will not include the rotation and position of the
349  // attachment point (e.g. your head when a sensor is in your
350  // hair attached to your scull. Your hair will turn with
351  // your head but the sensor will stay with your (global)
352  // avatar rotation and position.
353  // Position of a sensor in a child prim attached to an avatar
354  // will be still wrong.
355  ScenePresence avatar = m_CmdManager.m_ScriptEngine.World.GetScenePresence(SensePoint.ParentGroup.AttachedAvatar);
356 
357  // Don't proceed if the avatar for this attachment has since been removed from the scene.
358  if (avatar == null)
359  return sensedEntities;
360 
361  fromRegionPos = avatar.AbsolutePosition;
362  q = avatar.Rotation;
363  }
364 
366  LSL_Types.Vector3 forward_dir = (new LSL_Types.Vector3(1, 0, 0) * r);
367  double mag_fwd = LSL_Types.Vector3.Mag(forward_dir);
368 
369  Vector3 ZeroVector = new Vector3(0, 0, 0);
370 
371  bool nameSearch = !string.IsNullOrEmpty(ts.name);
372 
373  foreach (EntityBase ent in Entities)
374  {
375  bool keep = true;
376 
377  if (nameSearch && ent.Name != ts.name) // Wrong name and it is a named search
378  continue;
379 
380  if (ent.IsDeleted) // taken so long to do this it has gone from the scene
381  continue;
382 
383  if (!(ent is SceneObjectGroup)) // dont bother if it is a pesky avatar
384  continue;
385  toRegionPos = ent.AbsolutePosition;
386 
387  // Calculation is in line for speed
388  dx = toRegionPos.X - fromRegionPos.X;
389  dy = toRegionPos.Y - fromRegionPos.Y;
390  dz = toRegionPos.Z - fromRegionPos.Z;
391 
392  // Weed out those that will not fit in a cube the size of the range
393  // no point calculating if they are within a sphere the size of the range
394  // if they arent even in the cube
395  if (Math.Abs(dx) > ts.range || Math.Abs(dy) > ts.range || Math.Abs(dz) > ts.range)
396  dis = ts.range + 1.0;
397  else
398  dis = Math.Sqrt(dx * dx + dy * dy + dz * dz);
399 
400  if (keep && dis <= ts.range && ts.host.UUID != ent.UUID)
401  {
402  // In Range and not the object containing the script, is it the right Type ?
403  objtype = 0;
404 
405  part = ((SceneObjectGroup)ent).RootPart;
406  if (part.ParentGroup.RootPart.Shape.PCode != (byte)PCode.Tree &&
407  part.ParentGroup.RootPart.Shape.PCode != (byte)PCode.NewTree &&
408  part.ParentGroup.AttachmentPoint != 0) // Attached so ignore
409  continue;
410 
411  if (part.Inventory.ContainsScripts())
412  {
413  objtype |= ACTIVE | SCRIPTED; // Scripted and active. It COULD have one hidden ...
414  }
415  else
416  {
417  if (ent.Velocity.Equals(ZeroVector))
418  {
419  objtype |= PASSIVE; // Passive non-moving
420  }
421  else
422  {
423  objtype |= ACTIVE; // moving so active
424  }
425  }
426 
427  // If any of the objects attributes match any in the requested scan type
428  if (((ts.type & objtype) != 0))
429  {
430  // Right type too, what about the other params , key and name ?
431  if (ts.arc < Math.PI)
432  {
433  // not omni-directional. Can you see it ?
434  // vec forward_dir = llRot2Fwd(llGetRot())
435  // vec obj_dir = toRegionPos-fromRegionPos
436  // dot=dot(forward_dir,obj_dir)
437  // mag_fwd = mag(forward_dir)
438  // mag_obj = mag(obj_dir)
439  // ang = acos(dot /(mag_fwd*mag_obj))
440  double ang_obj = 0;
441  try
442  {
443  Vector3 diff = toRegionPos - fromRegionPos;
444  double dot = LSL_Types.Vector3.Dot(forward_dir, diff);
445  double mag_obj = LSL_Types.Vector3.Mag(diff);
446  ang_obj = Math.Acos(dot / (mag_fwd * mag_obj));
447  }
448  catch
449  {
450  }
451 
452  if (ang_obj > ts.arc) keep = false;
453  }
454 
455  if (keep == true)
456  {
457  // add distance for sorting purposes later
458  sensedEntities.Add(new SensedEntity(dis, ent.UUID));
459  }
460  }
461  }
462  }
463  return sensedEntities;
464  }
465 
466  private List<SensedEntity> doAgentSensor(SensorInfo ts)
467  {
468  List<SensedEntity> sensedEntities = new List<SensedEntity>();
469 
470  // If nobody about quit fast
471  if (m_CmdManager.m_ScriptEngine.World.GetRootAgentCount() == 0)
472  return sensedEntities;
473 
474  SceneObjectPart SensePoint = ts.host;
475  Vector3 fromRegionPos = SensePoint.GetWorldPosition();
476 
477  Quaternion q = SensePoint.GetWorldRotation();
478  if (SensePoint.ParentGroup.IsAttachment)
479  {
480  // In attachments, rotate the sensor cone with the
481  // avatar rotation. This may include a nonzero elevation if
482  // in mouselook.
483  // This will not include the rotation and position of the
484  // attachment point (e.g. your head when a sensor is in your
485  // hair attached to your scull. Your hair will turn with
486  // your head but the sensor will stay with your (global)
487  // avatar rotation and position.
488  // Position of a sensor in a child prim attached to an avatar
489  // will be still wrong.
490  ScenePresence avatar = m_CmdManager.m_ScriptEngine.World.GetScenePresence(SensePoint.ParentGroup.AttachedAvatar);
491 
492  // Don't proceed if the avatar for this attachment has since been removed from the scene.
493  if (avatar == null)
494  return sensedEntities;
495  fromRegionPos = avatar.AbsolutePosition;
496  q = avatar.Rotation;
497  }
498 
500  LSL_Types.Vector3 forward_dir = (new LSL_Types.Vector3(1, 0, 0) * r);
501  double mag_fwd = LSL_Types.Vector3.Mag(forward_dir);
502  bool attached = (SensePoint.ParentGroup.AttachmentPoint != 0);
503  Vector3 toRegionPos;
504  double dis;
505 
506  Action<ScenePresence> senseEntity = new Action<ScenePresence>(presence =>
507  {
508 // m_log.DebugFormat(
509 // "[SENSOR REPEAT]: Inspecting scene presence {0}, type {1} on sensor sweep for {2}, type {3}",
510 // presence.Name, presence.PresenceType, ts.name, ts.type);
511 
512  if ((ts.type & NPC) == 0 && presence.PresenceType == PresenceType.Npc)
513  {
514  INPC npcData = m_npcModule.GetNPC(presence.UUID, presence.Scene);
515  if (npcData == null || !npcData.SenseAsAgent)
516  {
517 // m_log.DebugFormat(
518 // "[SENSOR REPEAT]: Discarding NPC {0} from agent sense sweep for script item id {1}",
519 // presence.Name, ts.itemID);
520  return;
521  }
522  }
523 
524  if ((ts.type & AGENT) == 0)
525  {
526  if (presence.PresenceType == PresenceType.User)
527  {
528  return;
529  }
530  else
531  {
532  INPC npcData = m_npcModule.GetNPC(presence.UUID, presence.Scene);
533  if (npcData != null && npcData.SenseAsAgent)
534  {
535 // m_log.DebugFormat(
536 // "[SENSOR REPEAT]: Discarding NPC {0} from non-agent sense sweep for script item id {1}",
537 // presence.Name, ts.itemID);
538  return;
539  }
540  }
541  }
542 
543  if (presence.IsDeleted || presence.IsChildAgent || presence.GodLevel > 0.0)
544  return;
545 
546  // if the object the script is in is attached and the avatar is the owner
547  // then this one is not wanted
548  if (attached && presence.UUID == SensePoint.OwnerID)
549  return;
550 
551  toRegionPos = presence.AbsolutePosition;
552  dis = Util.GetDistanceTo(toRegionPos, fromRegionPos);
553  if (presence.IsSatOnObject && presence.ParentPart != null &&
554  presence.ParentPart.ParentGroup != null &&
555  presence.ParentPart.ParentGroup.RootPart != null)
556  {
557  Vector3 rpos = presence.ParentPart.ParentGroup.RootPart.AbsolutePosition;
558  double dis2 = Util.GetDistanceTo(rpos, fromRegionPos);
559  if (dis > dis2)
560  dis = dis2;
561  }
562 
563  // Disabled for now since all osNpc* methods check for appropriate ownership permission.
564  // Perhaps could be re-enabled as an NPC setting at some point since being able to make NPCs not
565  // sensed might be useful.
566 // if (presence.PresenceType == PresenceType.Npc && npcModule != null)
567 // {
568 // UUID npcOwner = npcModule.GetOwner(presence.UUID);
569 // if (npcOwner != UUID.Zero && npcOwner != SensePoint.OwnerID)
570 // return;
571 // }
572 
573  // are they in range
574  if (dis <= ts.range)
575  {
576  // Are they in the required angle of view
577  if (ts.arc < Math.PI)
578  {
579  // not omni-directional. Can you see it ?
580  // vec forward_dir = llRot2Fwd(llGetRot())
581  // vec obj_dir = toRegionPos-fromRegionPos
582  // dot=dot(forward_dir,obj_dir)
583  // mag_fwd = mag(forward_dir)
584  // mag_obj = mag(obj_dir)
585  // ang = acos(dot /(mag_fwd*mag_obj))
586  double ang_obj = 0;
587  try
588  {
589  LSL_Types.Vector3 obj_dir = new LSL_Types.Vector3(
590  toRegionPos - fromRegionPos);
591  double dot = LSL_Types.Vector3.Dot(forward_dir, obj_dir);
592  double mag_obj = LSL_Types.Vector3.Mag(obj_dir);
593  ang_obj = Math.Acos(dot / (mag_fwd * mag_obj));
594  }
595  catch
596  {
597  }
598  if (ang_obj <= ts.arc)
599  {
600  sensedEntities.Add(new SensedEntity(dis, presence.UUID));
601  }
602  }
603  else
604  {
605  sensedEntities.Add(new SensedEntity(dis, presence.UUID));
606  }
607  }
608  });
609 
610  // If this is an avatar sense by key try to get them directly
611  // rather than getting a list to scan through
612  if (ts.keyID != UUID.Zero)
613  {
614  ScenePresence sp;
615  // Try direct lookup by UUID
616  if (!m_CmdManager.m_ScriptEngine.World.TryGetScenePresence(ts.keyID, out sp))
617  return sensedEntities;
618  senseEntity(sp);
619  }
620  else if (!string.IsNullOrEmpty(ts.name))
621  {
622  ScenePresence sp;
623  // Try lookup by name will return if/when found
624  if (((ts.type & AGENT) != 0) && m_CmdManager.m_ScriptEngine.World.TryGetAvatarByName(ts.name, out sp))
625  senseEntity(sp);
626  if ((ts.type & AGENT_BY_USERNAME) != 0)
627  {
628  m_CmdManager.m_ScriptEngine.World.ForEachRootScenePresence(
629  delegate (ScenePresence ssp)
630  {
631  if (ssp.Lastname == "Resident")
632  {
633  if (ssp.Firstname.ToLower() == ts.name)
634  senseEntity(ssp);
635  return;
636  }
637  if (ssp.Name.Replace(" ", ".").ToLower() == ts.name)
638  senseEntity(ssp);
639  }
640  );
641  }
642 
643  return sensedEntities;
644  }
645  else
646  {
647  m_CmdManager.m_ScriptEngine.World.ForEachRootScenePresence(senseEntity);
648  }
649  return sensedEntities;
650  }
651 
652  public Object[] GetSerializationData(UUID itemID)
653  {
654  List<Object> data = new List<Object>();
655 
656  foreach (SensorInfo ts in SenseRepeaters)
657  {
658  if (ts.itemID == itemID)
659  {
660  data.Add(ts.interval);
661  data.Add(ts.name);
662  data.Add(ts.keyID);
663  data.Add(ts.type);
664  data.Add(ts.range);
665  data.Add(ts.arc);
666  }
667  }
668 
669  return data.ToArray();
670  }
671 
672  public void CreateFromData(uint localID, UUID itemID, UUID objectID,
673  Object[] data)
674  {
675  SceneObjectPart part =
676  m_CmdManager.m_ScriptEngine.World.GetSceneObjectPart(
677  objectID);
678 
679  if (part == null)
680  return;
681 
682  int idx = 0;
683 
684  while (idx < data.Length)
685  {
686  SensorInfo ts = new SensorInfo();
687 
688  ts.localID = localID;
689  ts.itemID = itemID;
690 
691  ts.interval = (double)data[idx];
692  ts.name = (string)data[idx+1];
693  ts.keyID = (UUID)data[idx+2];
694  ts.type = (int)data[idx+3];
695  ts.range = (double)data[idx+4];
696  ts.arc = (double)data[idx+5];
697  ts.host = part;
698 
699  ts.next =
700  DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
701 
702  AddSenseRepeater(ts);
703 
704  idx += 6;
705  }
706  }
707 
708  public List<SensorInfo> GetSensorInfo()
709  {
710  List<SensorInfo> retList = new List<SensorInfo>();
711 
712  lock (SenseRepeatListLock)
713  {
714  foreach (SensorInfo i in SenseRepeaters)
715  retList.Add(i.Clone());
716  }
717 
718  return retList;
719  }
720  }
721 }
IEntityInventory Inventory
This part's inventory
Handles LSL commands that takes long time and returns an event, for example timers, HTTP requests, etc.
void CreateFromData(uint localID, UUID itemID, UUID objectID, Object[] data)
bool SenseAsAgent
Should this NPC be sensed by LSL sensors as an 'agent' (interpreted here to mean a normal user) rathe...
Definition: INPCModule.cs:58
bool ContainsScripts()
Returns true if this inventory contains any scripts
A scene object group is conceptually an object in the scene. The object is constituted of SceneObject...
void SetSenseRepeatEvent(uint m_localID, UUID m_itemID, string name, UUID keyID, int type, double range, double arc, double sec, SceneObjectPart host)
Temporary interface. More methods to come at some point to make NPCs more object oriented rather than...
Definition: INPCModule.cs:50
PresenceType
Indicate the type of ScenePresence.
Definition: PresenceType.cs:34
bool IsDeleted
Signals whether this entity was in a scene but has since been removed from it.
Definition: EntityBase.cs:73
void SenseOnce(uint m_localID, UUID m_itemID, string name, UUID keyID, int type, double range, double arc, SceneObjectPart host)
uint AttachmentPoint
Attachment point of this scene object to an avatar.
void UnSetSenseRepeaterEvents(uint m_localID, UUID m_itemID)
virtual Vector3 Velocity
Current velocity of the entity.
Definition: EntityBase.cs:96
virtual string Name
The name of this entity
Definition: EntityBase.cs:65
Holds all the data required to execute a scripting event.
Definition: Helpers.cs:281
bool IsAttachment
Is this scene object acting as an attachment?