* Many llSensor() improvements, though sensoring isn't perfect yet
* thanks idb!
0.6.0-stable
Justin Clarke Casey 2008-10-09 22:41:07 +00:00
parent 60b86e1e62
commit 4e3bc9a63e
1 changed files with 244 additions and 75 deletions

View File

@ -45,10 +45,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
m_CmdManager = CmdManager; m_CmdManager = CmdManager;
} }
public Dictionary<uint, Dictionary<UUID, LSL_Types.list>> SenseEvents =
new Dictionary<uint, Dictionary<UUID, LSL_Types.list>>();
private Object SenseLock = new Object(); private Object SenseLock = new Object();
private const int AGENT = 1;
private const int ACTIVE = 2;
private const int PASSIVE = 4;
private const int SCRIPTED = 8;
private double maximumRange = 96.0;
private int maximumToReturn = 16;
// //
// SenseRepeater and Sensors // SenseRepeater and Sensors
// //
@ -87,6 +93,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
ts.name = name; ts.name = name;
ts.keyID = keyID; ts.keyID = keyID;
ts.type = type; ts.type = type;
if (range > maximumRange)
ts.range = maximumRange;
else
ts.range = range; ts.range = range;
ts.arc = arc; ts.arc = arc;
ts.host = host; ts.host = host;
@ -150,6 +159,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
ts.name = name; ts.name = name;
ts.keyID = keyID; ts.keyID = keyID;
ts.type = type; ts.type = type;
if (range > maximumRange)
ts.range = maximumRange;
else
ts.range = range; ts.range = range;
ts.arc = arc; ts.arc = arc;
ts.host = host; ts.host = host;
@ -158,13 +170,101 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
private void SensorSweep(SenseRepeatClass ts) private void SensorSweep(SenseRepeatClass ts)
{ {
SceneObjectPart SensePoint = ts.host; if (ts.host == null)
if (SensePoint == null)
{ {
return; return;
} }
LSL_Types.list SensedObjects = new LSL_Types.list();
// Is the sensor type is AGENT and not SCRIPTED then include agents
if ((ts.type & AGENT) != 0 && (ts.type & SCRIPTED) == 0)
{
doAgentSensor(ts, SensedObjects);
}
// If SCRIPTED or PASSIVE or ACTIVE check objects
if ((ts.type & SCRIPTED) != 0 || (ts.type & PASSIVE) != 0 || (ts.type & ACTIVE) != 0)
{
doObjectSensor(ts, SensedObjects);
}
lock (SenseLock)
{
if (SensedObjects.Length == 0)
{
// send a "no_sensor"
// Add it to queue
m_CmdManager.m_ScriptEngine.PostScriptEvent(ts.itemID,
new EventParams("no_sensor", new Object[0],
new DetectParams[0]));
}
else
{
// the sort is stride = 2 and ascending to get everything ordered by distance
SensedObjects = SensedObjects.Sort(2, 1);
int count = SensedObjects.Length;
int idx;
List<DetectParams> detected = new List<DetectParams>();
for (idx = 0; idx < count; idx++)
{
try
{
DetectParams detect = new DetectParams();
detect.Key = (UUID)(SensedObjects.Data[(idx * 2) + 1]);
detect.Populate(m_CmdManager.m_ScriptEngine.World);
detected.Add(detect);
}
catch (Exception)
{
// Ignore errors, the object has been deleted or the avatar has gone and
// there was a problem in detect.Populate so nothing added to the list
}
if (detected.Count == maximumToReturn)
break;
}
if (detected.Count == 0)
{
// To get here with zero in the list there must have been some sort of problem
// like the object being deleted or the avatar leaving to have caused some
// difficulty during the Populate above so fire a no_sensor event
m_CmdManager.m_ScriptEngine.PostScriptEvent(ts.itemID,
new EventParams("no_sensor", new Object[0],
new DetectParams[0]));
}
else
{
m_CmdManager.m_ScriptEngine.PostScriptEvent(ts.itemID,
new EventParams("sensor",
new Object[] {new LSL_Types.LSLInteger(detected.Count) },
detected.ToArray()));
}
}
}
}
private void doObjectSensor(SenseRepeatClass ts, LSL_Types.list SensedObjects)
{
List<EntityBase> Entities;
// If this is an object sense by key try to get it directly
// rather than getting a list to scan through
if (ts.keyID != UUID.Zero)
{
EntityBase e = null;
m_CmdManager.m_ScriptEngine.World.Entities.TryGetValue(ts.keyID, out e);
if (e == null)
return;
Entities = new List<EntityBase>();
Entities.Add(e);
}
else
{
Entities = m_CmdManager.m_ScriptEngine.World.GetEntities();
}
SceneObjectPart SensePoint = ts.host;
Vector3 sensorPos = SensePoint.AbsolutePosition; Vector3 sensorPos = SensePoint.AbsolutePosition;
Vector3 regionPos = new Vector3(m_CmdManager.m_ScriptEngine.World.RegionInfo.RegionLocX * Constants.RegionSize, m_CmdManager.m_ScriptEngine.World.RegionInfo.RegionLocY * Constants.RegionSize, 0); Vector3 regionPos = new Vector3(m_CmdManager.m_ScriptEngine.World.RegionInfo.RegionLocX * Constants.RegionSize, m_CmdManager.m_ScriptEngine.World.RegionInfo.RegionLocY * Constants.RegionSize, 0);
Vector3 fromRegionPos = sensorPos + regionPos; Vector3 fromRegionPos = sensorPos + regionPos;
@ -174,37 +274,55 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
LSL_Types.Vector3 forward_dir = (new LSL_Types.Vector3(1, 0, 0) * r); LSL_Types.Vector3 forward_dir = (new LSL_Types.Vector3(1, 0, 0) * r);
double mag_fwd = LSL_Types.Vector3.Mag(forward_dir); double mag_fwd = LSL_Types.Vector3.Mag(forward_dir);
// Here we should do some smart culling ... Vector3 ZeroVector = new Vector3(0, 0, 0);
// math seems quicker than strings so try that first
LSL_Types.list SensedObjects = new LSL_Types.list();
LSL_Types.Vector3 ZeroVector = new LSL_Types.Vector3(0, 0, 0);
foreach (EntityBase ent in m_CmdManager.m_ScriptEngine.World.Entities.Values) bool nameSearch = (ts.name != null && ts.name != "");
SceneObjectGroup group;
foreach (EntityBase ent in Entities)
{ {
bool keep = true;
if (nameSearch && ent.Name != ts.name) // Wrong name and it is a named search
continue;
if (ent.IsDeleted) // taken so long to do this it has gone from the scene
continue;
if (!(ent is SceneObjectGroup)) // dont bother if it is a pesky avatar
continue;
Vector3 toRegionPos = ent.AbsolutePosition + regionPos; Vector3 toRegionPos = ent.AbsolutePosition + regionPos;
double dis = Math.Abs((double)Util.GetDistanceTo(toRegionPos, fromRegionPos)); double dis = Math.Abs((double)Util.GetDistanceTo(toRegionPos, fromRegionPos));
if (dis <= ts.range) if (keep && dis <= ts.range && ts.host.UUID != ent.UUID)
{ {
// In Range, is it the right Type ? // In Range and not the object containing the script, is it the right Type ?
int objtype = 0; int objtype = 0;
if (m_CmdManager.m_ScriptEngine.World.GetScenePresence(ent.UUID) != null) objtype |= 0x01; // actor SceneObjectPart part = ((SceneObjectGroup)ent).RootPart;
if (ent.Velocity.Equals(ZeroVector)) if (part.AttachmentPoint != 0) // Attached so ignore
objtype |= 0x04; // passive non-moving continue;
else
objtype |= 0x02; // active moving
SceneObjectPart part = m_CmdManager.m_ScriptEngine.World.GetSceneObjectPart(ent.UUID); if (part.ContainsScripts())
if (part != null && part.ContainsScripts()) objtype |= 0x08; // Scripted. It COULD have one hidden ...
if (((ts.type & objtype) != 0) || ((ts.type & objtype) == ts.type))
{ {
// docs claim AGENT|ACTIVE should find agent objects OR active objects objtype |= ACTIVE | SCRIPTED; // Scripted and active. It COULD have one hidden ...
// so the bitwise AND with object type should be non-zero }
else
{
if (ent.Velocity.Equals(ZeroVector))
{
objtype |= PASSIVE; // Passive non-moving
}
else
{
objtype |= ACTIVE; // moving so active
}
}
// If any of the objects attributes match any in the requested scan type
if (((ts.type & objtype) != 0))
{
// Right type too, what about the other params , key and name ? // Right type too, what about the other params , key and name ?
bool keep = true;
if (ts.arc < Math.PI) if (ts.arc < Math.PI)
{ {
// not omni-directional. Can you see it ? // not omni-directional. Can you see it ?
@ -230,67 +348,118 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins
if (ang_obj > ts.arc) keep = false; if (ang_obj > ts.arc) keep = false;
} }
if (keep && (ts.keyID != UUID.Zero) && (ts.keyID != ent.UUID)) if (keep == true)
{ {
keep = false; // add distance for sorting purposes later
} SensedObjects.Add(new LSL_Types.LSLFloat(dis));
SensedObjects.Add(ent.UUID);
if (keep && (ts.name.Length > 0))
{
if (ts.name != ent.Name)
{
keep = false;
} }
} }
if (keep == true) SensedObjects.Add(ent.UUID);
} }
} }
} }
lock (SenseLock) private void doAgentSensor(SenseRepeatClass ts, LSL_Types.list SensedObjects)
{ {
// Create object if it doesn't exist List<ScenePresence> Presences;
if (SenseEvents.ContainsKey(ts.localID) == false)
{
SenseEvents.Add(ts.localID, new Dictionary<UUID, LSL_Types.list>());
}
// clear if previous traces exist
Dictionary<UUID, LSL_Types.list> Obj;
SenseEvents.TryGetValue(ts.localID, out Obj);
if (Obj.ContainsKey(ts.itemID) == true)
Obj.Remove(ts.itemID);
// note list may be zero length // If this is an avatar sense by key try to get them directly
Obj.Add(ts.itemID, SensedObjects); // rather than getting a list to scan through
if (ts.keyID != UUID.Zero)
if (SensedObjects.Length == 0)
{ {
// send a "no_sensor" ScenePresence p = m_CmdManager.m_ScriptEngine.World.GetScenePresence(ts.keyID);
// Add it to queue if (p == null)
m_CmdManager.m_ScriptEngine.PostScriptEvent(ts.itemID, return;
new EventParams("no_sensor", new Object[0], Presences = new List<ScenePresence>();
new DetectParams[0])); Presences.Add(p);
} }
else else
{ {
DetectParams[] detect = Presences = m_CmdManager.m_ScriptEngine.World.GetScenePresences();
new DetectParams[SensedObjects.Length]; }
int idx; // If nobody about quit fast
for (idx = 0; idx < SensedObjects.Length; idx++) if (Presences.Count == 0)
return;
SceneObjectPart SensePoint = ts.host;
Vector3 sensorPos = SensePoint.AbsolutePosition;
Vector3 regionPos = new Vector3(m_CmdManager.m_ScriptEngine.World.RegionInfo.RegionLocX * Constants.RegionSize, m_CmdManager.m_ScriptEngine.World.RegionInfo.RegionLocY * Constants.RegionSize, 0);
Vector3 fromRegionPos = sensorPos + regionPos;
Quaternion q = SensePoint.RotationOffset;
LSL_Types.Quaternion r = new LSL_Types.Quaternion(q.X, q.Y, q.Z, q.W);
LSL_Types.Vector3 forward_dir = (new LSL_Types.Vector3(1, 0, 0) * r);
double mag_fwd = LSL_Types.Vector3.Mag(forward_dir);
bool attached = (SensePoint.AttachmentPoint != 0);
bool nameSearch = (ts.name != null && ts.name != "");
foreach (ScenePresence presence in Presences)
{ {
detect[idx] = new DetectParams(); bool keep = true;
detect[idx].Key=(UUID)(SensedObjects.Data[idx]);
detect[idx].Populate(m_CmdManager.m_ScriptEngine.World); if (presence.IsDeleted)
continue;
if (presence.IsChildAgent)
keep = false;
Vector3 toRegionPos = presence.AbsolutePosition + regionPos;
double dis = Math.Abs(Util.GetDistanceTo(toRegionPos, fromRegionPos));
// are they in range
if (keep && dis <= ts.range)
{
// if the object the script is in is attached and the avatar is the owner
// then this one is not wanted
if (attached && presence.UUID == SensePoint.OwnerID)
keep = false;
// check the name if needed
if (keep && nameSearch && ts.name != presence.Name)
keep = false;
// Are they in the required angle of view
if (keep && ts.arc < Math.PI)
{
// not omni-directional. Can you see it ?
// vec forward_dir = llRot2Fwd(llGetRot())
// vec obj_dir = toRegionPos-fromRegionPos
// dot=dot(forward_dir,obj_dir)
// mag_fwd = mag(forward_dir)
// mag_obj = mag(obj_dir)
// ang = acos(dot /(mag_fwd*mag_obj))
double ang_obj = 0;
try
{
Vector3 diff = toRegionPos - fromRegionPos;
LSL_Types.Vector3 obj_dir = new LSL_Types.Vector3(diff.X, diff.Y, diff.Z);
double dot = LSL_Types.Vector3.Dot(forward_dir, obj_dir);
double mag_obj = LSL_Types.Vector3.Mag(obj_dir);
ang_obj = Math.Acos(dot / (mag_fwd * mag_obj));
}
catch
{
}
if (ang_obj > ts.arc) keep = false;
}
} }
m_CmdManager.m_ScriptEngine.PostScriptEvent(ts.itemID, // Do not report gods, not even minor ones
new EventParams("sensor", if (keep && presence.GodLevel > 0.0)
new Object[] { keep = false;
new LSL_Types.LSLInteger(SensedObjects.Length) },
detect)); if (keep) // add to list with distance
{
SensedObjects.Add(new LSL_Types.LSLFloat(dis));
SensedObjects.Add(presence.UUID);
} }
// If this is a search by name and we have just found it then no more to do
if (nameSearch && ts.name == presence.Name)
return;
} }
} }