Merge branch 'master' of melanie@opensimulator.org:/var/git/opensim

user_profiles
Melanie 2013-01-16 16:52:57 +00:00
commit 9c99ed26eb
36 changed files with 839 additions and 161 deletions

View File

@ -77,6 +77,7 @@ namespace OpenSim.Framework.Servers.HttpServer
// protected HttpListener m_httpListener;
protected CoolHTTPListener m_httpListener2;
protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>();
protected Dictionary<string, JsonRPCMethod> jsonRpcHandlers = new Dictionary<string, JsonRPCMethod>();
protected Dictionary<string, bool> m_rpcHandlersKeepAlive = new Dictionary<string, bool>();
protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/
protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
@ -217,6 +218,37 @@ namespace OpenSim.Framework.Servers.HttpServer
return new List<string>(m_rpcHandlers.Keys);
}
// JsonRPC
public bool AddJsonRPCHandler(string method, JsonRPCMethod handler)
{
lock(jsonRpcHandlers)
{
jsonRpcHandlers.Add(method, handler);
}
return true;
}
public JsonRPCMethod GetJsonRPCHandler(string method)
{
lock (jsonRpcHandlers)
{
if (jsonRpcHandlers.ContainsKey(method))
{
return jsonRpcHandlers[method];
}
else
{
return null;
}
}
}
public List<string> GetJsonRpcHandlerKeys()
{
lock (jsonRpcHandlers)
return new List<string>(jsonRpcHandlers.Keys);
}
public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler)
{
//m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName);
@ -557,9 +589,17 @@ namespace OpenSim.Framework.Servers.HttpServer
buffer = HandleLLSDRequests(request, response);
break;
case "application/json-rpc":
if (DebugLevel >= 3)
LogIncomingToContentTypeHandler(request);
buffer = HandleJsonRpcRequests(request, response);
break;
case "text/xml":
case "application/xml":
case "application/json":
default:
//m_log.Info("[Debug BASE HTTP SERVER]: in default handler");
// Point of note.. the DoWeHaveA methods check for an EXACT path
@ -985,6 +1025,74 @@ namespace OpenSim.Framework.Servers.HttpServer
return buffer;
}
// JsonRpc (v2.0 only)
private byte[] HandleJsonRpcRequests(OSHttpRequest request, OSHttpResponse response)
{
Stream requestStream = request.InputStream;
JsonRpcResponse jsonRpcResponse = new JsonRpcResponse();
OSDMap jsonRpcRequest = null;
try
{
jsonRpcRequest = (OSDMap)OSDParser.DeserializeJson(requestStream);
}
catch (LitJson.JsonException e)
{
jsonRpcResponse.Error.Code = ErrorCode.InternalError;
jsonRpcResponse.Error.Message = e.Message;
}
requestStream.Close();
if (jsonRpcRequest != null)
{
if (jsonRpcRequest.ContainsKey("jsonrpc") || jsonRpcRequest["jsonrpc"].AsString() == "2.0")
{
jsonRpcResponse.JsonRpc = "2.0";
// If we have no id, then it's a "notification"
if (jsonRpcRequest.ContainsKey("id"))
{
jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
}
string methodname = jsonRpcRequest["method"];
JsonRPCMethod method;
if (jsonRpcHandlers.ContainsKey(methodname))
{
lock(jsonRpcHandlers)
{
jsonRpcHandlers.TryGetValue(methodname, out method);
}
method(jsonRpcRequest, ref jsonRpcResponse);
}
else // Error no hanlder defined for requested method
{
jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
jsonRpcResponse.Error.Message = string.Format ("No handler defined for {0}", methodname);
}
}
else // not json-rpc 2.0 could be v1
{
jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
jsonRpcResponse.Error.Message = "Must be valid json-rpc 2.0 see: http://www.jsonrpc.org/specification";
if (jsonRpcRequest.ContainsKey("id"))
jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
}
}
response.KeepAlive = true;
string responseData = string.Empty;
responseData = jsonRpcResponse.Serialize();
byte[] buffer = Encoding.UTF8.GetBytes(responseData);
return buffer;
}
private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response)
{
//m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request");

View File

@ -97,6 +97,8 @@ namespace OpenSim.Framework.Servers.HttpServer
bool AddXmlRPCHandler(string method, XmlRpcMethod handler);
bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive);
bool AddJsonRPCHandler(string method, JsonRPCMethod handler);
/// <summary>
/// Gets the XML RPC handler for given method name
/// </summary>

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Net;
using OpenMetaverse.StructuredData;
namespace OpenSim.Framework.Servers.HttpServer
{
public delegate void JsonRPCMethod(OSDMap jsonRpcRequest, ref JsonRpcResponse response);
}

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Net;
using OpenMetaverse.StructuredData;
namespace OpenSim.Framework.Servers.HttpServer
{
public sealed class ErrorCode
{
private ErrorCode() {}
public const int ParseError = -32700;
public const int InvalidRequest = -32600;
public const int MethodNotFound = -32601;
public const int InvalidParams = -32602;
public const int InternalError = -32604;
}
public class JsonRpcError
{
internal OSDMap Error = new OSDMap();
public int Code
{
get
{
if (Error.ContainsKey("code"))
return Error["code"].AsInteger();
else
return 0;
}
set
{
Error["code"] = OSD.FromInteger(value);
}
}
public string Message
{
get
{
if (Error.ContainsKey("message"))
return Error["message"].AsString();
else
return null;
}
set
{
Error["message"] = OSD.FromString(value);
}
}
public OSD Data
{
get; set;
}
}
public class JsonRpcResponse
{
public string JsonRpc
{
get
{
return Reply["jsonrpc"].AsString();
}
set
{
Reply["jsonrpc"] = OSD.FromString(value);
}
}
public string Id
{
get
{
return Reply["id"].AsString();
}
set
{
Reply["id"] = OSD.FromString(value);
}
}
public OSD Result
{
get; set;
}
public JsonRpcError Error
{
get; set;
}
public OSDMap Reply = new OSDMap();
public JsonRpcResponse()
{
Error = new JsonRpcError();
}
public string Serialize()
{
if (Result != null)
Reply["result"] = Result;
if (Error.Code != 0)
{
Reply["error"] = (OSD)Error.Error;
}
string result = string.Empty;
try
{
result = OSDParser.SerializeJsonString(Reply);
}
catch
{
}
return result;
}
}
}

View File

@ -231,6 +231,10 @@ namespace OpenSim.Framework.Servers
foreach (String s in httpServer.GetHTTPHandlerKeys())
handlers.AppendFormat("\t{0} {1}\n", s, (poll.Contains(s) ? "(poll service)" : string.Empty));
handlers.AppendFormat("* JSONRPC:\n");
foreach (String s in httpServer.GetJsonRpcHandlerKeys())
handlers.AppendFormat("\t{0}\n", s);
// handlers.AppendFormat("* Agent:\n");
// foreach (String s in httpServer.GetAgentHandlerKeys())
// handlers.AppendFormat("\t{0}\n", s);

View File

@ -6427,20 +6427,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion
AgentRequestSit handlerAgentRequestSit = OnAgentRequestSit;
if (handlerAgentRequestSit != null)
if (!(agentRequestSit.AgentData == null
|| agentRequestSit.TargetObject == null
|| agentRequestSit.TargetObject.TargetID == null
|| agentRequestSit.TargetObject.Offset == null))
{
var sp = m_scene.GetScenePresence(agentRequestSit.AgentData.AgentID);
if (sp == null || sp.ParentID != 0) // ignore packet if agent is already sitting
return true;
if (handlerAgentRequestSit != null)
handlerAgentRequestSit(this, agentRequestSit.AgentData.AgentID,
agentRequestSit.TargetObject.TargetID, agentRequestSit.TargetObject.Offset);
}
}
return true;
}

View File

@ -212,11 +212,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
protected override GridRegion GetFinalDestination(GridRegion region)
{
int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID);
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionID, flags);
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionName, flags);
if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0)
{
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region {0} is hyperlink", region.RegionID);
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region is hyperlink");
GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID);
if (real_destination != null)
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: GetFinalDestination serveruri -> {0}", real_destination.ServerURI);

View File

@ -1737,6 +1737,21 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="itemBase"></param>
/// <returns>The part where the script was rezzed if successful. False otherwise.</returns>
public SceneObjectPart RezNewScript(UUID agentID, InventoryItemBase itemBase)
{
return RezNewScript(
agentID,
itemBase,
"default\n{\n state_entry()\n {\n llSay(0, \"Script running\");\n }\n}");
}
/// <summary>
/// Rez a new script from nothing with given script text.
/// </summary>
/// <param name="remoteClient"></param>
/// <param name="itemBase">Template item.</param>
/// <param name="scriptText"></param>
/// <returns>The part where the script was rezzed if successful. False otherwise.</returns>
public SceneObjectPart RezNewScript(UUID agentID, InventoryItemBase itemBase, string scriptText)
{
// The part ID is the folder ID!
SceneObjectPart part = GetSceneObjectPart(itemBase.Folder);
@ -1757,9 +1772,14 @@ namespace OpenSim.Region.Framework.Scenes
return null;
}
AssetBase asset = CreateAsset(itemBase.Name, itemBase.Description, (sbyte)itemBase.AssetType,
Encoding.ASCII.GetBytes("default\n{\n state_entry()\n {\n llSay(0, \"Script running\");\n }\n}"),
AssetBase asset
= CreateAsset(
itemBase.Name,
itemBase.Description,
(sbyte)itemBase.AssetType,
Encoding.ASCII.GetBytes(scriptText),
agentID);
AssetService.Store(asset);
TaskInventoryItem taskItem = new TaskInventoryItem();

View File

@ -1954,6 +1954,10 @@ namespace OpenSim.Region.Framework.Scenes
{
if (ParentID != 0)
{
var targetPart = m_scene.GetSceneObjectPart(targetID);
if (targetPart != null && targetPart.LocalId == ParentID)
return; // already sitting here, ignore
StandUp();
}

View File

@ -166,7 +166,7 @@ public override BulletWorld Initialize(Vector3 maxPosition, ConfigurationParamet
// If Debug logging level, enable logging from the unmanaged code
m_DebugLogCallbackHandle = null;
if (BSScene.m_log.IsDebugEnabled || PhysicsScene.PhysicsLogging.Enabled)
if (BSScene.m_log.IsDebugEnabled && PhysicsScene.PhysicsLogging.Enabled)
{
BSScene.m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", BSScene.LogHeader);
if (PhysicsScene.PhysicsLogging.Enabled)
@ -212,6 +212,19 @@ public override void Shutdown(BulletWorld world)
{
BulletWorldUnman worldu = world as BulletWorldUnman;
BSAPICPP.Shutdown2(worldu.ptr);
if (m_paramsHandle.IsAllocated)
{
m_paramsHandle.Free();
}
if (m_collisionArrayPinnedHandle.IsAllocated)
{
m_collisionArrayPinnedHandle.Free();
}
if (m_updateArrayPinnedHandle.IsAllocated)
{
m_updateArrayPinnedHandle.Free();
}
}
public override bool PushUpdate(BulletBody obj)

View File

@ -215,7 +215,7 @@ public sealed class BSCharacter : BSPhysObject
// Add special movement force to allow avatars to walk up stepped surfaces.
moveForce += WalkUpStairs();
DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce);
// DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce);
PhysicsScene.PE.ApplyCentralImpulse(PhysBody, moveForce);
});
}
@ -855,7 +855,10 @@ public sealed class BSCharacter : BSPhysObject
_rotationalVelocity = entprop.RotationalVelocity;
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
PositionSanityCheck(true);
if (PositionSanityCheck(true))
{
entprop.Position = _position;
}
// remember the current and last set values
LastEntityProperties = CurrentEntityProperties;

View File

@ -124,9 +124,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
static readonly float PIOverTwo = ((float)Math.PI) / 2f;
// For debugging, flags to turn on and off individual corrections.
private bool enableAngularVerticalAttraction = true;
private bool enableAngularDeflection = true;
private bool enableAngularBanking = true;
private bool enableAngularVerticalAttraction;
private bool enableAngularDeflection;
private bool enableAngularBanking;
public BSDynamics(BSScene myScene, BSPrim myPrim)
{
@ -141,8 +141,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
public void SetupVehicleDebugging()
{
enableAngularVerticalAttraction = true;
enableAngularDeflection = true;
enableAngularBanking = true;
enableAngularDeflection = false;
enableAngularBanking = false;
if (BSParam.VehicleDebuggingEnabled != ConfigurationParameters.numericFalse)
{
enableAngularVerticalAttraction = false;
@ -649,6 +649,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
private Quaternion m_knownOrientation;
private Vector3 m_knownRotationalVelocity;
private Vector3 m_knownRotationalForce;
private Vector3 m_knownRotationalImpulse;
private Vector3 m_knownForwardVelocity; // vehicle relative forward speed
private const int m_knownChangedPosition = 1 << 0;
@ -658,9 +659,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
private const int m_knownChangedOrientation = 1 << 4;
private const int m_knownChangedRotationalVelocity = 1 << 5;
private const int m_knownChangedRotationalForce = 1 << 6;
private const int m_knownChangedTerrainHeight = 1 << 7;
private const int m_knownChangedWaterLevel = 1 << 8;
private const int m_knownChangedForwardVelocity = 1 << 9;
private const int m_knownChangedRotationalImpulse = 1 << 7;
private const int m_knownChangedTerrainHeight = 1 << 8;
private const int m_knownChangedWaterLevel = 1 << 9;
private const int m_knownChangedForwardVelocity = 1 <<10;
private void ForgetKnownVehicleProperties()
{
@ -700,6 +702,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
PhysicsScene.PE.SetInterpolationAngularVelocity(Prim.PhysBody, m_knownRotationalVelocity);
}
if ((m_knownChanged & m_knownChangedRotationalImpulse) != 0)
Prim.ApplyTorqueImpulse((Vector3)m_knownRotationalImpulse, true /*inTaintTime*/);
if ((m_knownChanged & m_knownChangedRotationalForce) != 0)
{
Prim.AddAngularForce((Vector3)m_knownRotationalForce, false /*pushForce*/, true /*inTaintTime*/);
@ -843,6 +848,17 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_knownChanged |= m_knownChangedRotationalForce;
m_knownHas |= m_knownChangedRotationalForce;
}
private void VehicleAddRotationalImpulse(Vector3 pImpulse)
{
if ((m_knownHas & m_knownChangedRotationalImpulse) == 0)
{
m_knownRotationalImpulse = Vector3.Zero;
m_knownHas |= m_knownChangedRotationalImpulse;
}
m_knownRotationalImpulse += pImpulse;
m_knownChanged |= m_knownChangedRotationalImpulse;
}
// Vehicle relative forward velocity
private Vector3 VehicleForwardVelocity
{
@ -1031,16 +1047,32 @@ namespace OpenSim.Region.Physics.BulletSPlugin
else
{
// Error is positive if below the target and negative if above.
float verticalError = m_VhoverTargetHeight - VehiclePosition.Z;
float verticalCorrectionVelocity = verticalError / m_VhoverTimescale * pTimestep;
Vector3 hpos = VehiclePosition;
float verticalError = m_VhoverTargetHeight - hpos.Z;
float verticalCorrection = verticalError / m_VhoverTimescale;
verticalCorrection *= m_VhoverEfficiency;
hpos.Z += verticalCorrection;
VehiclePosition = hpos;
// Since we are hovering, we need to do the opposite of falling -- get rid of world Z
Vector3 vel = VehicleVelocity;
vel.Z = 0f;
VehicleVelocity = vel;
/*
float verticalCorrectionVelocity = verticalError / m_VhoverTimescale;
Vector3 verticalCorrection = new Vector3(0f, 0f, verticalCorrectionVelocity);
verticalCorrection *= m_vehicleMass;
// TODO: implement m_VhoverEfficiency correctly
VehicleAddForceImpulse(new Vector3(0f, 0f, verticalCorrectionVelocity));
VehicleAddForceImpulse(verticalCorrection);
*/
VDetailLog("{0}, MoveLinear,hover,pos={1},eff={2},hoverTS={3},height={4},target={5},err={6},corrVel={7}",
VDetailLog("{0}, MoveLinear,hover,pos={1},eff={2},hoverTS={3},height={4},target={5},err={6},corr={7}",
Prim.LocalID, VehiclePosition, m_VhoverEfficiency,
m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight,
verticalError, verticalCorrectionVelocity);
verticalError, verticalCorrection);
}
}
@ -1128,8 +1160,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
if (!Prim.IsColliding && VehicleVelocity.Z > 0.1)
{
// Get rid of any of the velocity vector that is pushing us up.
VehicleVelocity += new Vector3(0, 0, -VehicleVelocity.Z);
float upVelocity = VehicleVelocity.Z;
VehicleVelocity += new Vector3(0, 0, -upVelocity);
/*
// If we're pointed up into the air, we should nose down
Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation;
// The rotation around the Y axis is pitch up or down
@ -1143,11 +1177,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
VDetailLog("{0}, MoveLinear,limitMotorUp,newVel={1},pntDir={2},corrFrc={3},aCorr={4}",
Prim.LocalID, VehicleVelocity, pointingDirection, angularCorrectionForce, angularCorrectionVector);
}
else
{
VDetailLog("{0}, MoveLinear,limitMotorUp,newVel={1},pntDir={2}",
Prim.LocalID, VehicleVelocity, pointingDirection);
}
*/
VDetailLog("{0}, MoveLinear,limitMotorUp,collide={1},upVel={2},newVel={3}",
Prim.LocalID, Prim.IsColliding, upVelocity, VehicleVelocity);
}
}
}

View File

@ -108,8 +108,8 @@ public sealed class BSLinksetCompound : BSLinkset
// Schedule a refresh to happen after all the other taint processing.
private void ScheduleRebuild(BSPhysObject requestor)
{
DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2}",
requestor.LocalID, Rebuilding, HasAnyChildren);
DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}",
requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren));
// When rebuilding, it is possible to set properties that would normally require a rebuild.
// If already rebuilding, don't request another rebuild.
// If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding.
@ -195,8 +195,11 @@ public sealed class BSLinksetCompound : BSLinkset
&& PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
{
// TODO: replace this with are calculation of the child prim's orientation and pos.
updated.LinksetInfo = null;
ScheduleRebuild(updated);
// TODO: for the moment, don't rebuild the compound shape.
// This is often just the car turning its wheels. When we can just reorient the one
// member shape of the compound shape, the overhead of rebuilding won't be a problem.
// updated.LinksetInfo = null;
// ScheduleRebuild(updated);
}
}
@ -308,7 +311,7 @@ public sealed class BSLinksetCompound : BSLinkset
else
{
// Rebuild the compound shape with the child removed
ScheduleRebuild(child);
ScheduleRebuild(LinksetRoot);
}
}
return;

View File

@ -94,16 +94,16 @@ public static class BSParam
public static float PID_D { get; private set; } // derivative
public static float PID_P { get; private set; } // proportional
// Various constants that come from that other virtual world that shall not be named
// Various constants that come from that other virtual world that shall not be named.
public const float MinGravityZ = -1f;
public const float MaxGravityZ = 28f;
public const float MinFriction = 0f;
public const float MaxFriction = 255f;
public const float MinDensity = 0f;
public const float MinDensity = 0.01f;
public const float MaxDensity = 22587f;
public const float MinRestitution = 0f;
public const float MaxRestitution = 1f;
public const float MaxAddForceMagnitude = 20000f;
public const float MaxAddForceMagnitude = 20f;
// ===========================================================================
public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
@ -318,13 +318,13 @@ public static class BSParam
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularSleepingThreshold=x;}, p, l, v); },
(s,o,v) => { s.PE.SetSleepingThresholds(o.PhysBody, v, v); } ),
new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
0f, // set to zero to disable
0.3f, // set to zero to disable
(s,cf,p,v) => { CcdMotionThreshold = cf.GetFloat(p, v); },
(s) => { return CcdMotionThreshold; },
(s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdMotionThreshold=x;}, p, l, v); },
(s,o,v) => { s.PE.SetCcdMotionThreshold(o.PhysBody, v); } ),
new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
0f,
0.2f,
(s,cf,p,v) => { CcdSweptSphereRadius = cf.GetFloat(p, v); },
(s) => { return CcdSweptSphereRadius; },
(s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdSweptSphereRadius=x;}, p, l, v); },
@ -465,7 +465,7 @@ public static class BSParam
(s) => { return s.UnmanagedParams[0].shouldSplitSimulationIslands; },
(s,p,l,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = v; } ),
new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching",
ConfigurationParameters.numericFalse,
ConfigurationParameters.numericTrue,
(s,cf,p,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
(s) => { return s.UnmanagedParams[0].shouldEnableFrictionCaching; },
(s,p,l,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = v; } ),

View File

@ -442,7 +442,8 @@ public sealed class BSShapeCollection : IDisposable
return ret;
}
// Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'.
// Create a mesh, hull or native shape.
// Return 'true' if the prim's shape was changed.
public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
{
bool ret = false;
@ -472,7 +473,7 @@ public sealed class BSShapeCollection : IDisposable
if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}",
prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type);
// It doesn't look like Bullet scales spheres so make sure the scales are all equal
// It doesn't look like Bullet scales native spheres so make sure the scales are all equal
if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
&& pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
{
@ -484,9 +485,9 @@ public sealed class BSShapeCollection : IDisposable
{
ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE,
FixedShapeKey.KEY_SPHERE, shapeCallback);
if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.PhysShape);
}
if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}",
prim.LocalID, forceRebuild, ret, prim.PhysShape);
}
if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
{
@ -498,9 +499,9 @@ public sealed class BSShapeCollection : IDisposable
{
ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX,
FixedShapeKey.KEY_BOX, shapeCallback);
if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.PhysShape);
}
if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}",
prim.LocalID, forceRebuild, ret, prim.PhysShape);
}
}
@ -513,6 +514,7 @@ public sealed class BSShapeCollection : IDisposable
return ret;
}
// return 'true' if the prim's shape was changed.
public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
{
@ -872,8 +874,7 @@ public sealed class BSShapeCollection : IDisposable
{
prim.LastAssetBuildFailed = true;
BSPhysObject xprim = prim;
DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}",
LogHeader, prim.LocalID, prim.LastAssetBuildFailed);
DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lastFailed={1}", prim.LocalID, prim.LastAssetBuildFailed);
Util.FireAndForget(delegate
{
RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
@ -882,19 +883,34 @@ public sealed class BSShapeCollection : IDisposable
BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
{
if (!yprim.BaseShape.SculptEntry)
return;
if (yprim.BaseShape.SculptTexture.ToString() != asset.ID)
return;
bool assetFound = false; // DEBUG DEBUG
string mismatchIDs = String.Empty; // DEBUG DEBUG
if (yprim.BaseShape.SculptEntry)
{
if (yprim.BaseShape.SculptTexture.ToString() == asset.ID)
{
yprim.BaseShape.SculptData = asset.Data;
// This will cause the prim to see that the filler shape is not the right
// one and try again to build the object.
// No race condition with the normal shape setting since the rebuild is at taint time.
yprim.ForceBodyShapeRebuild(false);
yprim.ForceBodyShapeRebuild(false /* inTaintTime */);
assetFound = true;
}
else
{
mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID;
}
}
DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}",
yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs );
});
}
else
{
PhysicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}",
LogHeader, PhysicsScene.Name);
}
});
}
else
@ -906,9 +922,9 @@ public sealed class BSShapeCollection : IDisposable
}
}
// While we figure out the real problem, stick in a simple box for the object.
BulletShape fillinShape =
BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
// While we wait for the mesh defining asset to be loaded, stick in a simple box for the object.
BulletShape fillinShape = BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
DetailLog("{0},BSShapeCollection.VerifyMeshCreated,boxTempShape", prim.LocalID);
return fillinShape;
}

View File

@ -1,7 +1,14 @@
CURRENT PRIORITIES
=================================================
Mantis 6040 script http://opensimulator.org/mantis/view.php?id=6040
Msg Kayaker on OSGrid when working
Teravus llMoveToTarget script debug
Mixing of hover, buoyancy/gravity, moveToTarget, into one force
Boats floating at proper level
Nebadon vehicles turning funny in arena
limitMotorUp calibration (more down?)
llRotLookAt
llLookAt
Vehicle angular vertical attraction
Vehicle angular deflection
Preferred orientation angular correction fix
@ -9,8 +16,6 @@ vehicle angular banking
Avatars walking up stairs (HALF DONE)
Radius of the capsule affects ability to climb edges.
Vehicle movement on terrain smoothness
Surfboard go wonky when turning
Angular motor direction is global coordinates rather than local coordinates?
Boats float low in the water (DONE)
Avatar movement
flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE)
@ -27,6 +32,10 @@ Add material densities to the material types
CRASHES
=================================================
Crazyness during 20130115 office hours was PositionAdjustUnderground for both char and prim
m1:logs/20130115.0934/physics-BulletSim-20130115083613.log
Creation of Neb's terrain made the terrain "disappear". Everything started to fall
and then get restored to be above terrain.
20121129.1411: editting/moving phys object across region boundries causes crash
getPos-> btRigidBody::upcast -> getBodyType -> BOOM
20121128.1600: mesh object not rezzing (no physics mesh).
@ -111,6 +120,8 @@ Physical and phantom will drop through the terrain
LINKSETS
======================================================
Editing a child of a linkset causes the child to go phantom
Move a child prim once when it is physical and can never move it again without it going phantom
Offset the center of the linkset to be the geometric center of all the prims
Not quite the same as the center-of-gravity
Linksets should allow collisions to individual children
@ -133,6 +144,10 @@ Eliminate collisions between objects in a linkset. (LinksetConstraint)
MORE
======================================================
Create tests for different interface components
Have test objects/scripts measure themselves and turn color if correct/bad
Test functions in SL and calibrate correctness there
Create auto rezzer and tracker to run through the tests
Use the HACD convex hull routine in Bullet rather than the C# version.
Do we need to do convex hulls all the time? Can complex meshes be left meshes?
There is some problem with meshes and collisions
@ -167,6 +182,7 @@ Enforce physical parameter min/max:
Restitution [0, 1]
http://wiki.secondlife.com/wiki/Physics_Material_Settings_test
Avatar attachments have no mass? http://forums-archive.secondlife.com/54/f0/31796/1.html
Keep avatar scaling correct. http://pennycow.blogspot.fr/2011/07/matter-of-scale.html
INTERNAL IMPROVEMENT/CLEANUP
=================================================
@ -288,3 +304,6 @@ Disable activity of passive linkset children. (DONE)
around and need to be phantomized so they don't collide, ...
Remove HeightmapInfo from terrain specification (DONE)
Since C++ code does not need terrain height, this structure et al are not needed.
Surfboard go wonky when turning (DONE)
Angular motor direction is global coordinates rather than local coordinates?
(Resolution: made angular motor direction correct coordinate system)

View File

@ -26,9 +26,11 @@
*/
using System;
using System.Threading;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared;
namespace OpenSim.Region.ScriptEngine.Interfaces
{
@ -38,11 +40,12 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
/// Initialize the API
/// </summary>
/// <remarks>
/// Each API has an identifier, which is used to load the
/// proper runtime assembly at load time.
/// <param name='engine'>/param>
/// <param name='part'></param>
/// <param name='item'></param>
void Initialize(IScriptEngine engine, SceneObjectPart part, TaskInventoryItem item);
/// Each API has an identifier, which is used to load the proper runtime assembly at load time.
/// <param name='scriptEngine'>/param>
/// <param name='host'>/param>
/// <param name='item'>/param>
/// <param name='coopSleepHandle'>/param>
void Initialize(
IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item, EventWaitHandle coopSleepHandle);
}
}

View File

@ -28,9 +28,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using OpenMetaverse;
using log4net;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Interfaces;
@ -105,6 +107,11 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
/// </summary>
long MeasurementPeriodExecutionTime { get; }
/// <summary>
/// Scene part in which this script instance is contained.
/// </summary>
SceneObjectPart Part { get; }
IScriptEngine Engine { get; }
UUID AppDomain { get; set; }
string PrimName { get; }
@ -124,6 +131,12 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
uint LocalID { get; }
UUID AssetID { get; }
/// <summary>
/// Inventory item containing the script used.
/// </summary>
TaskInventoryItem ScriptTask { get; }
Queue EventQueue { get; }
/// <summary>

View File

@ -83,9 +83,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public class LSL_Api : MarshalByRefObject, ILSL_Api, IScriptApi
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected IScriptEngine m_ScriptEngine;
protected SceneObjectPart m_host;
/// <summary>
/// Used for script sleeps when we are using co-operative script termination.
/// </summary>
/// <remarks>null if co-operative script termination is not active</remarks>
EventWaitHandle m_coopSleepHandle;
/// <summary>
/// The item that hosts this script
/// </summary>
@ -110,24 +117,28 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
protected int EMAIL_PAUSE_TIME = 20; // documented delay value for smtp.
protected ISoundModule m_SoundModule = null;
public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, TaskInventoryItem item)
public void Initialize(
IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item, EventWaitHandle coopSleepHandle)
{
m_ScriptEngine = ScriptEngine;
m_ScriptEngine = scriptEngine;
m_host = host;
m_item = item;
m_coopSleepHandle = coopSleepHandle;
LoadLimits(); // read script limits from config.
LoadConfig();
m_TransferModule =
m_ScriptEngine.World.RequestModuleInterface<IMessageTransferModule>();
m_UrlModule = m_ScriptEngine.World.RequestModuleInterface<IUrlModule>();
m_SoundModule = m_ScriptEngine.World.RequestModuleInterface<ISoundModule>();
AsyncCommands = new AsyncCommandManager(ScriptEngine);
AsyncCommands = new AsyncCommandManager(m_ScriptEngine);
}
/* load configuration items that affect script, object and run-time behavior. */
private void LoadLimits()
/// <summary>
/// Load configuration items that affect script, object and run-time behavior. */
/// </summary>
private void LoadConfig()
{
m_ScriptDelayFactor =
m_ScriptEngine.Config.GetFloat("ScriptDelayFactor", 1.0f);
@ -141,12 +152,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
m_ScriptEngine.Config.GetInt("NotecardLineReadCharsMax", 255);
if (m_notecardLineReadCharsMax > 65535)
m_notecardLineReadCharsMax = 65535;
// load limits for particular subsystems.
IConfig SMTPConfig;
if ((SMTPConfig = m_ScriptEngine.ConfigSource.Configs["SMTP"]) != null) {
// there's an smtp config, so load in the snooze time.
EMAIL_PAUSE_TIME = SMTPConfig.GetInt("email_pause_time", EMAIL_PAUSE_TIME);
}
// Rezzing an object with a velocity can create recoil. This feature seems to have been
// removed from recent versions of SL. The code computes recoil (vel*mass) and scales
// it by this factor. May be zero to turn off recoil all together.
@ -171,7 +184,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
delay = (int)((float)delay * m_ScriptDelayFactor);
if (delay == 0)
return;
Sleep(delay);
}
protected virtual void Sleep(int delay)
{
if (m_coopSleepHandle == null)
System.Threading.Thread.Sleep(delay);
else if (m_coopSleepHandle.WaitOne(delay))
throw new ScriptCoopStopException();
}
public Scene World
@ -2910,7 +2932,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{
// m_log.Info("llSleep snoozing " + sec + "s.");
m_host.AddScriptLPS(1);
Thread.Sleep((int)(sec * 1000));
Sleep((int)(sec * 1000));
}
public LSL_Float llGetMass()

View File

@ -30,6 +30,7 @@ using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Remoting.Lifetime;
using System.Threading;
using OpenMetaverse;
using Nini.Config;
using OpenSim;
@ -61,9 +62,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
internal bool m_LSFunctionsEnabled = false;
internal IScriptModuleComms m_comms = null;
public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, TaskInventoryItem item)
public void Initialize(
IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item, EventWaitHandle coopSleepHandle)
{
m_ScriptEngine = ScriptEngine;
m_ScriptEngine = scriptEngine;
m_host = host;
if (m_ScriptEngine.Config.GetBoolean("AllowLightShareFunctions", false))
@ -92,10 +94,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
get { return m_ScriptEngine.World; }
}
//
//Dumps an error message on the debug console.
//
/// <summary>
/// Dumps an error message on the debug console.
/// </summary>
internal void LSShoutError(string message)
{
if (message.Length > 1023)

View File

@ -30,6 +30,7 @@ using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Remoting.Lifetime;
using System.Threading;
using OpenMetaverse;
using Nini.Config;
using OpenSim;
@ -61,9 +62,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
internal bool m_MODFunctionsEnabled = false;
internal IScriptModuleComms m_comms = null;
public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, TaskInventoryItem item)
public void Initialize(
IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item, EventWaitHandle coopSleepHandle)
{
m_ScriptEngine = ScriptEngine;
m_ScriptEngine = scriptEngine;
m_host = host;
m_item = item;

View File

@ -142,9 +142,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
protected IUrlModule m_UrlModule = null;
public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, TaskInventoryItem item)
public void Initialize(
IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item, EventWaitHandle coopSleepHandle)
{
m_ScriptEngine = ScriptEngine;
m_ScriptEngine = scriptEngine;
m_host = host;
m_item = item;

View File

@ -81,6 +81,24 @@ namespace OpenSim.Region.ScriptEngine.Shared
}
}
/// <summary>
/// Used to signal when the script is stopping in co-operation with the script engine
/// (instead of through Thread.Abort()).
/// </summary>
[Serializable]
public class ScriptCoopStopException : Exception
{
public ScriptCoopStopException()
{
}
protected ScriptCoopStopException(
SerializationInfo info,
StreamingContext context)
{
}
}
public class DetectParams
{
public const int AGENT = 1;

View File

@ -157,9 +157,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public UUID AppDomain { get; set; }
/// <summary>
/// Scene part in which this script instance is contained.
/// </summary>
public SceneObjectPart Part { get; private set; }
public string PrimName { get; private set; }
@ -203,16 +200,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public static readonly long MaxMeasurementPeriod = 30 * TimeSpan.TicksPerMinute;
private bool m_coopTermination;
private EventWaitHandle m_coopSleepHandle;
public void ClearQueue()
{
m_TimerQueued = false;
EventQueue.Clear();
}
public ScriptInstance(IScriptEngine engine, SceneObjectPart part,
UUID itemID, UUID assetID, string assembly,
AppDomain dom, string primName, string scriptName,
int startParam, bool postOnRez, StateSource stateSource,
public ScriptInstance(
IScriptEngine engine, SceneObjectPart part, TaskInventoryItem item,
int startParam, bool postOnRez,
int maxScriptQueue)
{
State = "default";
@ -220,32 +220,48 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
Engine = engine;
Part = part;
ItemID = itemID;
AssetID = assetID;
PrimName = primName;
ScriptName = scriptName;
m_Assembly = assembly;
ScriptTask = item;
// This is currently only here to allow regression tests to get away without specifying any inventory
// item when they are testing script logic that doesn't require an item.
if (ScriptTask != null)
{
ScriptName = ScriptTask.Name;
ItemID = ScriptTask.ItemID;
AssetID = ScriptTask.AssetID;
}
PrimName = part.ParentGroup.Name;
StartParam = startParam;
m_MaxScriptQueue = maxScriptQueue;
m_stateSource = stateSource;
m_postOnRez = postOnRez;
m_AttachedAvatar = Part.ParentGroup.AttachedAvatar;
m_RegionID = Part.ParentGroup.Scene.RegionInfo.RegionID;
lock (Part.TaskInventory)
if (Engine.Config.GetString("ScriptStopStrategy", "abort") == "co-op")
{
if (Part.TaskInventory.ContainsKey(ItemID))
{
ScriptTask = Part.TaskInventory[ItemID];
m_coopTermination = true;
m_coopSleepHandle = new AutoResetEvent(false);
}
}
/// <summary>
/// Load the script from an assembly into an AppDomain.
/// </summary>
/// <param name='dom'></param>
/// <param name='assembly'></param>
/// <param name='stateSource'></param>
public void Load(AppDomain dom, string assembly, StateSource stateSource)
{
m_Assembly = assembly;
m_stateSource = stateSource;
ApiManager am = new ApiManager();
foreach (string api in am.GetApis())
{
m_Apis[api] = am.CreateApi(api);
m_Apis[api].Initialize(engine, part, ScriptTask);
m_Apis[api].Initialize(Engine, Part, ScriptTask, m_coopSleepHandle);
}
try
@ -279,7 +295,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
// // m_log.Debug("[Script] Script instance created");
part.SetScriptEvents(ItemID,
Part.SetScriptEvents(ItemID,
(int)m_Script.GetStateEventFlags(State));
}
catch (Exception e)
@ -526,10 +542,35 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
}
// Wait for the current event to complete.
if (!m_InSelfDelete && workItem.Wait(new TimeSpan((long)timeout * 100000)))
if (!m_InSelfDelete)
{
if (!m_coopTermination)
{
// If we're not co-operative terminating then try and wait for the event to complete before stopping
if (workItem.Wait(new TimeSpan((long)timeout * 100000)))
return true;
}
else
{
m_log.DebugFormat(
"[SCRIPT INSTANCE]: Co-operatively stopping script {0} {1} in {2} {3}",
ScriptName, ItemID, PrimName, ObjectID);
// This will terminate the event on next handle check by the script.
m_coopSleepHandle.Set();
// For now, we will wait forever since the event should always cleanly terminate once LSL loop
// checking is implemented. May want to allow a shorter timeout option later.
if (workItem.Wait(TimeSpan.MaxValue))
{
m_log.DebugFormat(
"[SCRIPT INSTANCE]: Co-operatively stopped script {0} {1} in {2} {3}",
ScriptName, ItemID, PrimName, ObjectID);
return true;
}
}
}
lock (EventQueue)
{
@ -541,6 +582,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
// If the event still hasn't stopped and we the stop isn't the result of script or object removal, then
// forcibly abort the work item (this aborts the underlying thread).
// Co-operative termination should never reach this point.
if (!m_InSelfDelete)
{
m_log.DebugFormat(
@ -780,7 +822,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
m_InEvent = false;
m_CurrentEvent = String.Empty;
if ((!(e is TargetInvocationException) || (!(e.InnerException is SelfDeleteException) && !(e.InnerException is ScriptDeleteException))) && !(e is ThreadAbortException))
if ((!(e is TargetInvocationException)
|| (!(e.InnerException is SelfDeleteException)
&& !(e.InnerException is ScriptDeleteException)
&& !(e.InnerException is ScriptCoopStopException)))
&& !(e is ThreadAbortException))
{
try
{
@ -828,6 +874,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
m_InSelfDelete = true;
Part.Inventory.RemoveInventoryItem(ItemID);
}
else if ((e is TargetInvocationException) && (e.InnerException is ScriptCoopStopException))
{
m_log.DebugFormat(
"[SCRIPT INSTANCE]: Script {0}.{1} in event {2}, state {3} stopped co-operatively.",
PrimName, ScriptName, data.EventName, State);
}
}
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Threading;
using Nini.Config;
using NUnit.Framework;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.CoreModules.Scripting.WorldComm;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.ScriptEngine.XEngine;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.ScriptEngine.Shared.Instance.Tests
{
/// <summary>
/// Test that co-operative script thread termination is working correctly.
/// </summary>
[TestFixture]
public class CoopTerminationTests : OpenSimTestCase
{
private TestScene m_scene;
private OpenSim.Region.ScriptEngine.XEngine.XEngine m_xEngine;
private AutoResetEvent m_chatEvent = new AutoResetEvent(false);
private AutoResetEvent m_stoppedEvent = new AutoResetEvent(false);
private OSChatMessage m_osChatMessageReceived;
[TestFixtureSetUp]
public void Init()
{
//AppDomain.CurrentDomain.SetData("APPBASE", Environment.CurrentDirectory + "/bin");
// Console.WriteLine(AppDomain.CurrentDomain.BaseDirectory);
m_xEngine = new OpenSim.Region.ScriptEngine.XEngine.XEngine();
IniConfigSource configSource = new IniConfigSource();
IConfig startupConfig = configSource.AddConfig("Startup");
startupConfig.Set("DefaultScriptEngine", "XEngine");
IConfig xEngineConfig = configSource.AddConfig("XEngine");
xEngineConfig.Set("Enabled", "true");
xEngineConfig.Set("StartDelay", "0");
// These tests will not run with AppDomainLoading = true, at least on mono. For unknown reasons, the call
// to AssemblyResolver.OnAssemblyResolve fails.
xEngineConfig.Set("AppDomainLoading", "false");
xEngineConfig.Set("ScriptStopStrategy", "co-op");
m_scene = new SceneHelpers().SetupScene("My Test", UUID.Random(), 1000, 1000, configSource);
SceneHelpers.SetupSceneModules(m_scene, configSource, m_xEngine);
m_scene.StartScripts();
}
/// <summary>
/// Test co-operative termination on derez of an object containing a script with a long-running event.
/// </summary>
/// <remarks>
/// TODO: Actually compiling the script is incidental to this test. Really want a way to compile test scripts
/// within the build itself.
/// </remarks>
[Test]
public void TestStopOnLongSleep()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
UUID userId = TestHelpers.ParseTail(0x1);
// UUID objectId = TestHelpers.ParseTail(0x100);
// UUID itemId = TestHelpers.ParseTail(0x3);
string itemName = "TestStopOnObjectDerezLongSleep() Item";
SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, userId, "TestStopOnObjectDerezLongSleep", 0x100);
m_scene.AddNewSceneObject(so, true);
InventoryItemBase itemTemplate = new InventoryItemBase();
// itemTemplate.ID = itemId;
itemTemplate.Name = itemName;
itemTemplate.Folder = so.UUID;
itemTemplate.InvType = (int)InventoryType.LSL;
m_scene.EventManager.OnChatFromWorld += OnChatFromWorld;
SceneObjectPart partWhereRezzed = m_scene.RezNewScript(userId, itemTemplate,
@"default
{
state_entry()
{
llSay(0, ""Thin Lizzy"");
llSleep(60);
}
}");
TaskInventoryItem rezzedItem = partWhereRezzed.Inventory.GetInventoryItem(itemName);
// Wait for the script to start the event before we try stopping it.
m_chatEvent.WaitOne(60000);
Console.WriteLine("Script started with message [{0}]", m_osChatMessageReceived.Message);
// FIXME: This is a very poor way of trying to avoid a low-probability race condition where the script
// executes llSay() but has not started the sleep before we try to stop it.
Thread.Sleep(1000);
// We need a way of carrying on if StopScript() fail, since it won't return if the script isn't actually
// stopped. This kind of multi-threading is far from ideal in a regression test.
new Thread(() => { m_xEngine.StopScript(rezzedItem.ItemID); m_stoppedEvent.Set(); }).Start();
if (!m_stoppedEvent.WaitOne(30000))
Assert.Fail("Script did not co-operatively stop.");
bool running;
TaskInventoryItem scriptItem = partWhereRezzed.Inventory.GetInventoryItem(itemName);
Assert.That(
SceneObjectPartInventory.TryGetScriptInstanceRunning(m_scene, scriptItem, out running), Is.True);
Assert.That(running, Is.False);
}
private void OnChatFromWorld(object sender, OSChatMessage oscm)
{
// Console.WriteLine("Got chat [{0}]", oscm.Message);
m_osChatMessageReceived = oscm;
m_chatEvent.Set();
}
}
}

View File

@ -41,6 +41,7 @@ using OpenSim.Region.OptionalModules.World.NPC;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
@ -93,7 +94,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
TaskInventoryHelpers.AddSceneObject(m_scene, so1.RootPart, inventoryItemName, itemId, userId);
LSL_Api api = new LSL_Api();
api.Initialize(m_engine, so1.RootPart, null);
api.Initialize(m_engine, so1.RootPart, null, null);
// Create a second object
SceneObjectGroup so2 = SceneHelpers.CreateSceneObject(1, userId, "so2", 0x100);
@ -126,7 +127,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
SceneObjectGroup so1 = SceneHelpers.CreateSceneObject(1, user1Id, "so1", 0x10);
m_scene.AddSceneObject(so1);
LSL_Api api = new LSL_Api();
api.Initialize(m_engine, so1.RootPart, null);
api.Initialize(m_engine, so1.RootPart, null, null);
// Create an object embedded inside the first
UUID itemId = TestHelpers.ParseTail(0x20);
@ -136,7 +137,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
SceneObjectGroup so2 = SceneHelpers.CreateSceneObject(1, user2Id, "so2", 0x100);
m_scene.AddSceneObject(so2);
LSL_Api api2 = new LSL_Api();
api2.Initialize(m_engine, so2.RootPart, null);
api2.Initialize(m_engine, so2.RootPart, null, null);
// *** Firstly, we test where llAllowInventoryDrop() has not been called. ***
api.llGiveInventory(so2.UUID.ToString(), inventoryItemName);

View File

@ -41,6 +41,7 @@ using OpenSim.Region.OptionalModules.World.NPC;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
@ -104,7 +105,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(grp2);
LSL_Api apiGrp1 = new LSL_Api();
apiGrp1.Initialize(m_engine, grp1.RootPart, grp1Item);
apiGrp1.Initialize(m_engine, grp1.RootPart, grp1Item, null);
apiGrp1.llCreateLink(grp2.UUID.ToString(), ScriptBaseClass.TRUE);
@ -131,7 +132,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
grp1Item.PermsMask |= ScriptBaseClass.PERMISSION_CHANGE_LINKS;
LSL_Api apiGrp1 = new LSL_Api();
apiGrp1.Initialize(m_engine, grp1.RootPart, grp1Item);
apiGrp1.Initialize(m_engine, grp1.RootPart, grp1Item, null);
apiGrp1.llBreakLink(2);

View File

@ -34,6 +34,7 @@ using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.Framework.Scenes;
using Nini.Config;
using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
using OpenMetaverse;
using OpenSim.Tests.Common.Mock;
@ -67,7 +68,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
engine.AddRegion(scene);
m_lslApi = new LSL_Api();
m_lslApi.Initialize(engine, part, null);
m_lslApi.Initialize(engine, part, null, null);
}
[Test]

View File

@ -33,6 +33,7 @@ using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.Framework.Scenes;
using Nini.Config;
using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
using OpenMetaverse;
using System;
@ -66,7 +67,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
engine.AddRegion(scene);
m_lslApi = new LSL_Api();
m_lslApi.Initialize(engine, part, null);
m_lslApi.Initialize(engine, part, null, null);
}
[Test]

View File

@ -41,6 +41,7 @@ using OpenSim.Region.OptionalModules.World.NPC;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
@ -93,7 +94,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null);
osslApi.Initialize(m_engine, part, null, null);
string notecardName = "appearanceNc";
@ -134,7 +135,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null);
osslApi.Initialize(m_engine, part, null, null);
string notecardName = "appearanceNc";

View File

@ -41,6 +41,7 @@ using OpenSim.Region.CoreModules.Framework.InventoryAccess;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
@ -98,9 +99,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
SceneObjectGroup inWorldObj = SceneHelpers.AddSceneObject(m_scene, "inWorldObj", ua1.PrincipalID);
TaskInventoryItem scriptItem = TaskInventoryHelpers.AddScript(m_scene, inWorldObj.RootPart);
new LSL_Api().Initialize(m_engine, inWorldObj.RootPart, scriptItem);
new LSL_Api().Initialize(m_engine, inWorldObj.RootPart, scriptItem, null);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, inWorldObj.RootPart, scriptItem);
osslApi.Initialize(m_engine, inWorldObj.RootPart, scriptItem, null);
// SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ua1.PrincipalID);
@ -144,9 +145,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
SceneObjectGroup inWorldObj = SceneHelpers.AddSceneObject(m_scene, "inWorldObj", ua1.PrincipalID);
TaskInventoryItem scriptItem = TaskInventoryHelpers.AddScript(m_scene, inWorldObj.RootPart);
new LSL_Api().Initialize(m_engine, inWorldObj.RootPart, scriptItem);
new LSL_Api().Initialize(m_engine, inWorldObj.RootPart, scriptItem, null);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, inWorldObj.RootPart, scriptItem);
osslApi.Initialize(m_engine, inWorldObj.RootPart, scriptItem, null);
// Create an object embedded inside the first
TaskInventoryHelpers.AddNotecard(
@ -192,12 +193,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
SceneObjectGroup inWorldObj = SceneHelpers.AddSceneObject(m_scene, "inWorldObj", ua1.PrincipalID);
TaskInventoryItem scriptItem = TaskInventoryHelpers.AddScript(m_scene, inWorldObj.RootPart);
new LSL_Api().Initialize(m_engine, inWorldObj.RootPart, scriptItem);
new LSL_Api().Initialize(m_engine, inWorldObj.RootPart, scriptItem, null);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, inWorldObj.RootPart, scriptItem);
osslApi.Initialize(m_engine, inWorldObj.RootPart, scriptItem, null);
// Create an object embedded inside the first
TaskInventoryHelpers.AddSceneObject(m_scene, inWorldObj.RootPart, taskInvObjItemName, taskInvObjItemId, ua1.PrincipalID);
TaskInventoryHelpers.AddSceneObject(
m_scene, inWorldObj.RootPart, taskInvObjItemName, taskInvObjItemId, ua1.PrincipalID);
ScenePresence sp2 = SceneHelpers.AddScenePresence(m_scene, ua2);

View File

@ -42,6 +42,7 @@ using OpenSim.Region.OptionalModules.World.NPC;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
@ -99,7 +100,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null);
osslApi.Initialize(m_engine, part, null, null);
string notecardName = "appearanceNc";
osslApi.osOwnerSaveAppearance(notecardName);
@ -125,7 +126,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, so.RootPart, null);
osslApi.Initialize(m_engine, so.RootPart, null, null);
bool gotExpectedException = false;
try
@ -160,7 +161,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null);
osslApi.Initialize(m_engine, part, null, null);
string notecardName = "appearanceNc";
osslApi.osOwnerSaveAppearance(notecardName);
@ -194,7 +195,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null);
osslApi.Initialize(m_engine, part, null, null);
osslApi.osOwnerSaveAppearance(firstAppearanceNcName);
@ -232,7 +233,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null);
osslApi.Initialize(m_engine, part, null, null);
osslApi.osOwnerSaveAppearance(firstAppearanceNcName);
@ -284,10 +285,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(otherSo);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null);
osslApi.Initialize(m_engine, part, null, null);
OSSL_Api otherOsslApi = new OSSL_Api();
otherOsslApi.Initialize(m_engine, otherPart, null);
otherOsslApi.Initialize(m_engine, otherPart, null, null);
string notecardName = "appearanceNc";
osslApi.osOwnerSaveAppearance(notecardName);
@ -331,7 +332,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null);
osslApi.Initialize(m_engine, part, null, null);
string notecardName = "appearanceNc";
osslApi.osOwnerSaveAppearance(notecardName);

View File

@ -1284,11 +1284,11 @@ namespace OpenSim.Region.ScriptEngine.XEngine
m_DomainScripts[appDomain].Add(itemID);
instance = new ScriptInstance(this, part,
itemID, assetID, assembly,
m_AppDomains[appDomain],
part.ParentGroup.RootPart.Name,
item.Name, startParam, postOnRez,
stateSource, m_MaxScriptQueue);
item,
startParam, postOnRez,
m_MaxScriptQueue);
instance.Load(m_AppDomains[appDomain], assembly, stateSource);
// if (DebugLevel >= 1)
// m_log.DebugFormat(
@ -1716,10 +1716,15 @@ namespace OpenSim.Region.ScriptEngine.XEngine
IScriptInstance instance = GetInstance(itemID);
if (instance != null)
{
instance.Stop(m_WaitForEventCompletionOnScriptStop);
}
else
{
// m_log.DebugFormat("[XENGINE]: Could not find script with ID {0} to stop in {1}", itemID, World.Name);
m_runFlags.AddOrUpdate(itemID, false, 240);
}
}
public DetectParams GetDetectParams(UUID itemID, int idx)
{

View File

@ -1,4 +1,4 @@
=== The Quick Guide to OpenSim Unit Testing ===
===== The Quick Guide to OpenSim Unit Testing ===
== Running Tests ==

View File

@ -929,18 +929,10 @@
MaxObjectMass = 10000.01
; Dynamic parameters
LinearDamping = 0.0
AngularDamping = 0.0
DeactivationTime = 0.2
CollisionMargin = 0.04
; Linkset constraint parameters
LinkImplementation = 1 ; 0=constraint, 1=compound
LinkConstraintUseFrameOffset = False
LinkConstraintEnableTransMotor = True
LinkConstraintTransMotorMaxVel = 5.0
LinkConstraintTransMotorMaxForce = 0.1
; Whether to mesh sculpties
MeshSculptedPrim = true

View File

@ -2417,7 +2417,9 @@
<Reference name="log4net" path="../../../../../bin/"/>
<Files>
<Match pattern="*.cs" recurse="true"/>
<Match pattern="*.cs" recurse="true">
<Exclude name="Tests" pattern="Tests"/>
</Match>
</Files>
</Project>
@ -3285,6 +3287,7 @@
<Reference name="OpenSim.Region.ScriptEngine.Shared"/>
<Reference name="OpenSim.Region.ScriptEngine.Shared.Api"/>
<Reference name="OpenSim.Region.ScriptEngine.Shared.Api.Runtime"/>
<Reference name="OpenSim.Region.ScriptEngine.Shared.Instance"/>
<Reference name="OpenSim.Region.ScriptEngine.XEngine"/>
<Reference name="OpenSim.Services.Interfaces"/>
<Reference name="OpenSim.Tests.Common"/>
@ -3308,6 +3311,7 @@
<!-- SADLY the way this works means you need to keep adding these paths -->
<Match path="Shared/Tests" pattern="*.cs" recurse="true"/>
<Match path="Shared/CodeTools/Tests" pattern="*.cs" recurse="true"/>
<Match path="Shared/Instance/Tests" pattern="*.cs" recurse="true"/>
<Match path="XEngine/Tests" pattern="*.cs" recurse="true"/>
</Files>
</Project>