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 HttpListener m_httpListener;
protected CoolHTTPListener m_httpListener2; protected CoolHTTPListener m_httpListener2;
protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>(); 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 Dictionary<string, bool> m_rpcHandlersKeepAlive = new Dictionary<string, bool>();
protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/ protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/
protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>(); 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); 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) public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler)
{ {
//m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName); //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName);
@ -556,10 +588,18 @@ namespace OpenSim.Framework.Servers.HttpServer
buffer = HandleLLSDRequests(request, response); buffer = HandleLLSDRequests(request, response);
break; break;
case "application/json-rpc":
if (DebugLevel >= 3)
LogIncomingToContentTypeHandler(request);
buffer = HandleJsonRpcRequests(request, response);
break;
case "text/xml": case "text/xml":
case "application/xml": case "application/xml":
case "application/json": case "application/json":
default: default:
//m_log.Info("[Debug BASE HTTP SERVER]: in default handler"); //m_log.Info("[Debug BASE HTTP SERVER]: in default handler");
// Point of note.. the DoWeHaveA methods check for an EXACT path // Point of note.. the DoWeHaveA methods check for an EXACT path
@ -985,6 +1025,74 @@ namespace OpenSim.Framework.Servers.HttpServer
return buffer; 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) private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response)
{ {
//m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request"); //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 AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive); bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive);
bool AddJsonRPCHandler(string method, JsonRPCMethod handler);
/// <summary> /// <summary>
/// Gets the XML RPC handler for given method name /// Gets the XML RPC handler for given method name
/// </summary> /// </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

@ -230,6 +230,10 @@ namespace OpenSim.Framework.Servers
List<String> poll = httpServer.GetPollServiceHandlerKeys(); List<String> poll = httpServer.GetPollServiceHandlerKeys();
foreach (String s in httpServer.GetHTTPHandlerKeys()) foreach (String s in httpServer.GetHTTPHandlerKeys())
handlers.AppendFormat("\t{0} {1}\n", s, (poll.Contains(s) ? "(poll service)" : string.Empty)); 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"); // handlers.AppendFormat("* Agent:\n");
// foreach (String s in httpServer.GetAgentHandlerKeys()) // foreach (String s in httpServer.GetAgentHandlerKeys())

View File

@ -6427,19 +6427,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion #endregion
AgentRequestSit handlerAgentRequestSit = OnAgentRequestSit; 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;
handlerAgentRequestSit(this, agentRequestSit.AgentData.AgentID, if (handlerAgentRequestSit != null)
agentRequestSit.TargetObject.TargetID, agentRequestSit.TargetObject.Offset); handlerAgentRequestSit(this, agentRequestSit.AgentData.AgentID,
} agentRequestSit.TargetObject.TargetID, agentRequestSit.TargetObject.Offset);
} }
return true; return true;
} }

View File

@ -212,11 +212,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
protected override GridRegion GetFinalDestination(GridRegion region) protected override GridRegion GetFinalDestination(GridRegion region)
{ {
int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID); 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) 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); GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID);
if (real_destination != null) if (real_destination != null)
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: GetFinalDestination serveruri -> {0}", real_destination.ServerURI); 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> /// <param name="itemBase"></param>
/// <returns>The part where the script was rezzed if successful. False otherwise.</returns> /// <returns>The part where the script was rezzed if successful. False otherwise.</returns>
public SceneObjectPart RezNewScript(UUID agentID, InventoryItemBase itemBase) 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! // The part ID is the folder ID!
SceneObjectPart part = GetSceneObjectPart(itemBase.Folder); SceneObjectPart part = GetSceneObjectPart(itemBase.Folder);
@ -1757,9 +1772,14 @@ namespace OpenSim.Region.Framework.Scenes
return null; return null;
} }
AssetBase asset = CreateAsset(itemBase.Name, itemBase.Description, (sbyte)itemBase.AssetType, AssetBase asset
Encoding.ASCII.GetBytes("default\n{\n state_entry()\n {\n llSay(0, \"Script running\");\n }\n}"), = CreateAsset(
agentID); itemBase.Name,
itemBase.Description,
(sbyte)itemBase.AssetType,
Encoding.ASCII.GetBytes(scriptText),
agentID);
AssetService.Store(asset); AssetService.Store(asset);
TaskInventoryItem taskItem = new TaskInventoryItem(); TaskInventoryItem taskItem = new TaskInventoryItem();

View File

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

View File

@ -166,7 +166,7 @@ public override BulletWorld Initialize(Vector3 maxPosition, ConfigurationParamet
// If Debug logging level, enable logging from the unmanaged code // If Debug logging level, enable logging from the unmanaged code
m_DebugLogCallbackHandle = null; 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); BSScene.m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", BSScene.LogHeader);
if (PhysicsScene.PhysicsLogging.Enabled) if (PhysicsScene.PhysicsLogging.Enabled)
@ -212,6 +212,19 @@ public override void Shutdown(BulletWorld world)
{ {
BulletWorldUnman worldu = world as BulletWorldUnman; BulletWorldUnman worldu = world as BulletWorldUnman;
BSAPICPP.Shutdown2(worldu.ptr); 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) 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. // Add special movement force to allow avatars to walk up stepped surfaces.
moveForce += WalkUpStairs(); 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); PhysicsScene.PE.ApplyCentralImpulse(PhysBody, moveForce);
}); });
} }
@ -855,7 +855,10 @@ public sealed class BSCharacter : BSPhysObject
_rotationalVelocity = entprop.RotationalVelocity; _rotationalVelocity = entprop.RotationalVelocity;
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds. // 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 // remember the current and last set values
LastEntityProperties = CurrentEntityProperties; LastEntityProperties = CurrentEntityProperties;

View File

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

View File

@ -108,8 +108,8 @@ public sealed class BSLinksetCompound : BSLinkset
// Schedule a refresh to happen after all the other taint processing. // Schedule a refresh to happen after all the other taint processing.
private void ScheduleRebuild(BSPhysObject requestor) private void ScheduleRebuild(BSPhysObject requestor)
{ {
DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2}", DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}",
requestor.LocalID, Rebuilding, HasAnyChildren); requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren));
// When rebuilding, it is possible to set properties that would normally require a rebuild. // When rebuilding, it is possible to set properties that would normally require a rebuild.
// If already rebuilding, don't request another 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. // 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)) && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
{ {
// TODO: replace this with are calculation of the child prim's orientation and pos. // TODO: replace this with are calculation of the child prim's orientation and pos.
updated.LinksetInfo = null; // TODO: for the moment, don't rebuild the compound shape.
ScheduleRebuild(updated); // 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 else
{ {
// Rebuild the compound shape with the child removed // Rebuild the compound shape with the child removed
ScheduleRebuild(child); ScheduleRebuild(LinksetRoot);
} }
} }
return; return;

View File

@ -94,16 +94,16 @@ public static class BSParam
public static float PID_D { get; private set; } // derivative public static float PID_D { get; private set; } // derivative
public static float PID_P { get; private set; } // proportional 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 MinGravityZ = -1f;
public const float MaxGravityZ = 28f; public const float MaxGravityZ = 28f;
public const float MinFriction = 0f; public const float MinFriction = 0f;
public const float MaxFriction = 255f; public const float MaxFriction = 255f;
public const float MinDensity = 0f; public const float MinDensity = 0.01f;
public const float MaxDensity = 22587f; public const float MaxDensity = 22587f;
public const float MinRestitution = 0f; public const float MinRestitution = 0f;
public const float MaxRestitution = 1f; 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); 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,p,l,v) => { s.UpdateParameterObject((x)=>{AngularSleepingThreshold=x;}, p, l, v); },
(s,o,v) => { s.PE.SetSleepingThresholds(o.PhysBody, v, v); } ), (s,o,v) => { s.PE.SetSleepingThresholds(o.PhysBody, v, v); } ),
new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , 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,cf,p,v) => { CcdMotionThreshold = cf.GetFloat(p, v); },
(s) => { return CcdMotionThreshold; }, (s) => { return CcdMotionThreshold; },
(s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdMotionThreshold=x;}, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdMotionThreshold=x;}, p, l, v); },
(s,o,v) => { s.PE.SetCcdMotionThreshold(o.PhysBody, v); } ), (s,o,v) => { s.PE.SetCcdMotionThreshold(o.PhysBody, v); } ),
new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
0f, 0.2f,
(s,cf,p,v) => { CcdSweptSphereRadius = cf.GetFloat(p, v); }, (s,cf,p,v) => { CcdSweptSphereRadius = cf.GetFloat(p, v); },
(s) => { return CcdSweptSphereRadius; }, (s) => { return CcdSweptSphereRadius; },
(s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdSweptSphereRadius=x;}, p, l, v); }, (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) => { return s.UnmanagedParams[0].shouldSplitSimulationIslands; },
(s,p,l,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = v; } ), (s,p,l,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = v; } ),
new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching", 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,cf,p,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
(s) => { return s.UnmanagedParams[0].shouldEnableFrictionCaching; }, (s) => { return s.UnmanagedParams[0].shouldEnableFrictionCaching; },
(s,p,l,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = v; } ), (s,p,l,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = v; } ),

View File

@ -442,7 +442,8 @@ public sealed class BSShapeCollection : IDisposable
return ret; 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) public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
{ {
bool ret = false; 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}", 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); 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) if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
&& pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) && 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, ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE,
FixedShapeKey.KEY_SPHERE, shapeCallback); 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) 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, ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX,
FixedShapeKey.KEY_BOX, shapeCallback); 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 ret;
} }
// return 'true' if the prim's shape was changed.
public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
{ {
@ -872,8 +874,7 @@ public sealed class BSShapeCollection : IDisposable
{ {
prim.LastAssetBuildFailed = true; prim.LastAssetBuildFailed = true;
BSPhysObject xprim = prim; BSPhysObject xprim = prim;
DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}", DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lastFailed={1}", prim.LocalID, prim.LastAssetBuildFailed);
LogHeader, prim.LocalID, prim.LastAssetBuildFailed);
Util.FireAndForget(delegate Util.FireAndForget(delegate
{ {
RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
@ -882,19 +883,34 @@ public sealed class BSShapeCollection : IDisposable
BSPhysObject yprim = xprim; // probably not necessary, but, just in case. BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
{ {
if (!yprim.BaseShape.SculptEntry) bool assetFound = false; // DEBUG DEBUG
return; string mismatchIDs = String.Empty; // DEBUG DEBUG
if (yprim.BaseShape.SculptTexture.ToString() != asset.ID) if (yprim.BaseShape.SculptEntry)
return; {
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 yprim.BaseShape.SculptData = asset.Data;
// one and try again to build the object. // This will cause the prim to see that the filler shape is not the right
// No race condition with the normal shape setting since the rebuild is at taint time. // one and try again to build the object.
yprim.ForceBodyShapeRebuild(false); // No race condition with the normal shape setting since the rebuild is at taint time.
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 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. // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object.
BulletShape fillinShape = BulletShape fillinShape = BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); DetailLog("{0},BSShapeCollection.VerifyMeshCreated,boxTempShape", prim.LocalID);
return fillinShape; return fillinShape;
} }

View File

@ -1,7 +1,14 @@
CURRENT PRIORITIES 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 Nebadon vehicles turning funny in arena
limitMotorUp calibration (more down?) limitMotorUp calibration (more down?)
llRotLookAt
llLookAt
Vehicle angular vertical attraction Vehicle angular vertical attraction
Vehicle angular deflection Vehicle angular deflection
Preferred orientation angular correction fix Preferred orientation angular correction fix
@ -9,8 +16,6 @@ vehicle angular banking
Avatars walking up stairs (HALF DONE) Avatars walking up stairs (HALF DONE)
Radius of the capsule affects ability to climb edges. Radius of the capsule affects ability to climb edges.
Vehicle movement on terrain smoothness 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) Boats float low in the water (DONE)
Avatar movement Avatar movement
flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE) 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 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 20121129.1411: editting/moving phys object across region boundries causes crash
getPos-> btRigidBody::upcast -> getBodyType -> BOOM getPos-> btRigidBody::upcast -> getBodyType -> BOOM
20121128.1600: mesh object not rezzing (no physics mesh). 20121128.1600: mesh object not rezzing (no physics mesh).
@ -111,6 +120,8 @@ Physical and phantom will drop through the terrain
LINKSETS 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 Offset the center of the linkset to be the geometric center of all the prims
Not quite the same as the center-of-gravity Not quite the same as the center-of-gravity
Linksets should allow collisions to individual children Linksets should allow collisions to individual children
@ -133,6 +144,10 @@ Eliminate collisions between objects in a linkset. (LinksetConstraint)
MORE 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. 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? Do we need to do convex hulls all the time? Can complex meshes be left meshes?
There is some problem with meshes and collisions There is some problem with meshes and collisions
@ -167,6 +182,7 @@ Enforce physical parameter min/max:
Restitution [0, 1] Restitution [0, 1]
http://wiki.secondlife.com/wiki/Physics_Material_Settings_test http://wiki.secondlife.com/wiki/Physics_Material_Settings_test
Avatar attachments have no mass? http://forums-archive.secondlife.com/54/f0/31796/1.html 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 INTERNAL IMPROVEMENT/CLEANUP
================================================= =================================================
@ -287,4 +303,7 @@ Disable activity of passive linkset children. (DONE)
Since the linkset is a compound object, the old prims are left lying Since the linkset is a compound object, the old prims are left lying
around and need to be phantomized so they don't collide, ... around and need to be phantomized so they don't collide, ...
Remove HeightmapInfo from terrain specification (DONE) Remove HeightmapInfo from terrain specification (DONE)
Since C++ code does not need terrain height, this structure et al are not needed. 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;
using System.Threading;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared;
namespace OpenSim.Region.ScriptEngine.Interfaces namespace OpenSim.Region.ScriptEngine.Interfaces
{ {
@ -38,11 +40,12 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
/// Initialize the API /// Initialize the API
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Each API has an identifier, which is used to load the /// Each API has an identifier, which is used to load the proper runtime assembly at load time.
/// proper runtime assembly at load time. /// <param name='scriptEngine'>/param>
/// <param name='engine'>/param> /// <param name='host'>/param>
/// <param name='part'></param> /// <param name='item'>/param>
/// <param name='item'></param> /// <param name='coopSleepHandle'>/param>
void Initialize(IScriptEngine engine, SceneObjectPart part, TaskInventoryItem item); void Initialize(
IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item, EventWaitHandle coopSleepHandle);
} }
} }

View File

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

View File

@ -83,9 +83,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public class LSL_Api : MarshalByRefObject, ILSL_Api, IScriptApi public class LSL_Api : MarshalByRefObject, ILSL_Api, IScriptApi
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected IScriptEngine m_ScriptEngine; protected IScriptEngine m_ScriptEngine;
protected SceneObjectPart m_host; 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> /// <summary>
/// The item that hosts this script /// The item that hosts this script
/// </summary> /// </summary>
@ -110,24 +117,28 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
protected int EMAIL_PAUSE_TIME = 20; // documented delay value for smtp. protected int EMAIL_PAUSE_TIME = 20; // documented delay value for smtp.
protected ISoundModule m_SoundModule = null; 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_host = host;
m_item = item; m_item = item;
m_coopSleepHandle = coopSleepHandle;
LoadLimits(); // read script limits from config. LoadConfig();
m_TransferModule = m_TransferModule =
m_ScriptEngine.World.RequestModuleInterface<IMessageTransferModule>(); m_ScriptEngine.World.RequestModuleInterface<IMessageTransferModule>();
m_UrlModule = m_ScriptEngine.World.RequestModuleInterface<IUrlModule>(); m_UrlModule = m_ScriptEngine.World.RequestModuleInterface<IUrlModule>();
m_SoundModule = m_ScriptEngine.World.RequestModuleInterface<ISoundModule>(); 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. */ /// <summary>
private void LoadLimits() /// Load configuration items that affect script, object and run-time behavior. */
/// </summary>
private void LoadConfig()
{ {
m_ScriptDelayFactor = m_ScriptDelayFactor =
m_ScriptEngine.Config.GetFloat("ScriptDelayFactor", 1.0f); m_ScriptEngine.Config.GetFloat("ScriptDelayFactor", 1.0f);
@ -141,12 +152,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
m_ScriptEngine.Config.GetInt("NotecardLineReadCharsMax", 255); m_ScriptEngine.Config.GetInt("NotecardLineReadCharsMax", 255);
if (m_notecardLineReadCharsMax > 65535) if (m_notecardLineReadCharsMax > 65535)
m_notecardLineReadCharsMax = 65535; m_notecardLineReadCharsMax = 65535;
// load limits for particular subsystems. // load limits for particular subsystems.
IConfig SMTPConfig; IConfig SMTPConfig;
if ((SMTPConfig = m_ScriptEngine.ConfigSource.Configs["SMTP"]) != null) { if ((SMTPConfig = m_ScriptEngine.ConfigSource.Configs["SMTP"]) != null) {
// there's an smtp config, so load in the snooze time. // there's an smtp config, so load in the snooze time.
EMAIL_PAUSE_TIME = SMTPConfig.GetInt("email_pause_time", EMAIL_PAUSE_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 // 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 // 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. // 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); delay = (int)((float)delay * m_ScriptDelayFactor);
if (delay == 0) if (delay == 0)
return; return;
System.Threading.Thread.Sleep(delay);
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 public Scene World
@ -2910,7 +2932,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{ {
// m_log.Info("llSleep snoozing " + sec + "s."); // m_log.Info("llSleep snoozing " + sec + "s.");
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
Thread.Sleep((int)(sec * 1000));
Sleep((int)(sec * 1000));
} }
public LSL_Float llGetMass() public LSL_Float llGetMass()

View File

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

View File

@ -30,6 +30,7 @@ using System.Reflection;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.Remoting.Lifetime; using System.Runtime.Remoting.Lifetime;
using System.Threading;
using OpenMetaverse; using OpenMetaverse;
using Nini.Config; using Nini.Config;
using OpenSim; using OpenSim;
@ -61,9 +62,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
internal bool m_MODFunctionsEnabled = false; internal bool m_MODFunctionsEnabled = false;
internal IScriptModuleComms m_comms = null; 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_host = host;
m_item = item; m_item = item;

View File

@ -142,9 +142,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
protected IUrlModule m_UrlModule = null; 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_host = host;
m_item = item; 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 class DetectParams
{ {
public const int AGENT = 1; public const int AGENT = 1;

View File

@ -157,9 +157,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public UUID AppDomain { get; set; } public UUID AppDomain { get; set; }
/// <summary>
/// Scene part in which this script instance is contained.
/// </summary>
public SceneObjectPart Part { get; private set; } public SceneObjectPart Part { get; private set; }
public string PrimName { get; private set; } public string PrimName { get; private set; }
@ -203,49 +200,68 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public static readonly long MaxMeasurementPeriod = 30 * TimeSpan.TicksPerMinute; public static readonly long MaxMeasurementPeriod = 30 * TimeSpan.TicksPerMinute;
private bool m_coopTermination;
private EventWaitHandle m_coopSleepHandle;
public void ClearQueue() public void ClearQueue()
{ {
m_TimerQueued = false; m_TimerQueued = false;
EventQueue.Clear(); EventQueue.Clear();
} }
public ScriptInstance(IScriptEngine engine, SceneObjectPart part, public ScriptInstance(
UUID itemID, UUID assetID, string assembly, IScriptEngine engine, SceneObjectPart part, TaskInventoryItem item,
AppDomain dom, string primName, string scriptName, int startParam, bool postOnRez,
int startParam, bool postOnRez, StateSource stateSource, int maxScriptQueue)
int maxScriptQueue)
{ {
State = "default"; State = "default";
EventQueue = new Queue(32); EventQueue = new Queue(32);
Engine = engine; Engine = engine;
Part = part; Part = part;
ItemID = itemID; ScriptTask = item;
AssetID = assetID;
PrimName = primName; // This is currently only here to allow regression tests to get away without specifying any inventory
ScriptName = scriptName; // item when they are testing script logic that doesn't require an item.
m_Assembly = assembly; if (ScriptTask != null)
{
ScriptName = ScriptTask.Name;
ItemID = ScriptTask.ItemID;
AssetID = ScriptTask.AssetID;
}
PrimName = part.ParentGroup.Name;
StartParam = startParam; StartParam = startParam;
m_MaxScriptQueue = maxScriptQueue; m_MaxScriptQueue = maxScriptQueue;
m_stateSource = stateSource;
m_postOnRez = postOnRez; m_postOnRez = postOnRez;
m_AttachedAvatar = Part.ParentGroup.AttachedAvatar; m_AttachedAvatar = Part.ParentGroup.AttachedAvatar;
m_RegionID = Part.ParentGroup.Scene.RegionInfo.RegionID; m_RegionID = Part.ParentGroup.Scene.RegionInfo.RegionID;
lock (Part.TaskInventory) if (Engine.Config.GetString("ScriptStopStrategy", "abort") == "co-op")
{ {
if (Part.TaskInventory.ContainsKey(ItemID)) m_coopTermination = true;
{ m_coopSleepHandle = new AutoResetEvent(false);
ScriptTask = Part.TaskInventory[ItemID];
}
} }
}
/// <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(); ApiManager am = new ApiManager();
foreach (string api in am.GetApis()) foreach (string api in am.GetApis())
{ {
m_Apis[api] = am.CreateApi(api); m_Apis[api] = am.CreateApi(api);
m_Apis[api].Initialize(engine, part, ScriptTask); m_Apis[api].Initialize(Engine, Part, ScriptTask, m_coopSleepHandle);
} }
try try
@ -279,7 +295,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
// // m_log.Debug("[Script] Script instance created"); // // m_log.Debug("[Script] Script instance created");
part.SetScriptEvents(ItemID, Part.SetScriptEvents(ItemID,
(int)m_Script.GetStateEventFlags(State)); (int)m_Script.GetStateEventFlags(State));
} }
catch (Exception e) catch (Exception e)
@ -526,9 +542,34 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
} }
// Wait for the current event to complete. // Wait for the current event to complete.
if (!m_InSelfDelete && workItem.Wait(new TimeSpan((long)timeout * 100000))) if (!m_InSelfDelete)
{ {
return true; 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) 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 // 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). // forcibly abort the work item (this aborts the underlying thread).
// Co-operative termination should never reach this point.
if (!m_InSelfDelete) if (!m_InSelfDelete)
{ {
m_log.DebugFormat( m_log.DebugFormat(
@ -780,7 +822,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
m_InEvent = false; m_InEvent = false;
m_CurrentEvent = String.Empty; 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 try
{ {
@ -828,6 +874,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
m_InSelfDelete = true; m_InSelfDelete = true;
Part.Inventory.RemoveInventoryItem(ItemID); 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.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared; using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api; using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Services.Interfaces; using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common; using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock; using OpenSim.Tests.Common.Mock;
@ -93,7 +94,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
TaskInventoryHelpers.AddSceneObject(m_scene, so1.RootPart, inventoryItemName, itemId, userId); TaskInventoryHelpers.AddSceneObject(m_scene, so1.RootPart, inventoryItemName, itemId, userId);
LSL_Api api = new LSL_Api(); 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 // Create a second object
SceneObjectGroup so2 = SceneHelpers.CreateSceneObject(1, userId, "so2", 0x100); 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); SceneObjectGroup so1 = SceneHelpers.CreateSceneObject(1, user1Id, "so1", 0x10);
m_scene.AddSceneObject(so1); m_scene.AddSceneObject(so1);
LSL_Api api = new LSL_Api(); 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 // Create an object embedded inside the first
UUID itemId = TestHelpers.ParseTail(0x20); UUID itemId = TestHelpers.ParseTail(0x20);
@ -136,7 +137,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
SceneObjectGroup so2 = SceneHelpers.CreateSceneObject(1, user2Id, "so2", 0x100); SceneObjectGroup so2 = SceneHelpers.CreateSceneObject(1, user2Id, "so2", 0x100);
m_scene.AddSceneObject(so2); m_scene.AddSceneObject(so2);
LSL_Api api2 = new LSL_Api(); 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. *** // *** Firstly, we test where llAllowInventoryDrop() has not been called. ***
api.llGiveInventory(so2.UUID.ToString(), inventoryItemName); 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.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared; using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api; using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Region.ScriptEngine.Shared.ScriptBase; using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
using OpenSim.Services.Interfaces; using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common; using OpenSim.Tests.Common;
@ -104,7 +105,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(grp2); m_scene.AddSceneObject(grp2);
LSL_Api apiGrp1 = new LSL_Api(); 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); apiGrp1.llCreateLink(grp2.UUID.ToString(), ScriptBaseClass.TRUE);
@ -131,7 +132,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
grp1Item.PermsMask |= ScriptBaseClass.PERMISSION_CHANGE_LINKS; grp1Item.PermsMask |= ScriptBaseClass.PERMISSION_CHANGE_LINKS;
LSL_Api apiGrp1 = new LSL_Api(); LSL_Api apiGrp1 = new LSL_Api();
apiGrp1.Initialize(m_engine, grp1.RootPart, grp1Item); apiGrp1.Initialize(m_engine, grp1.RootPart, grp1Item, null);
apiGrp1.llBreakLink(2); apiGrp1.llBreakLink(2);

View File

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

View File

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

View File

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

View File

@ -41,6 +41,7 @@ using OpenSim.Region.CoreModules.Framework.InventoryAccess;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared; using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api; using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Services.Interfaces; using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common; using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock; using OpenSim.Tests.Common.Mock;
@ -98,9 +99,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
SceneObjectGroup inWorldObj = SceneHelpers.AddSceneObject(m_scene, "inWorldObj", ua1.PrincipalID); SceneObjectGroup inWorldObj = SceneHelpers.AddSceneObject(m_scene, "inWorldObj", ua1.PrincipalID);
TaskInventoryItem scriptItem = TaskInventoryHelpers.AddScript(m_scene, inWorldObj.RootPart); 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(); 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); // 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); SceneObjectGroup inWorldObj = SceneHelpers.AddSceneObject(m_scene, "inWorldObj", ua1.PrincipalID);
TaskInventoryItem scriptItem = TaskInventoryHelpers.AddScript(m_scene, inWorldObj.RootPart); 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(); 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 // Create an object embedded inside the first
TaskInventoryHelpers.AddNotecard( TaskInventoryHelpers.AddNotecard(
@ -192,12 +193,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
SceneObjectGroup inWorldObj = SceneHelpers.AddSceneObject(m_scene, "inWorldObj", ua1.PrincipalID); SceneObjectGroup inWorldObj = SceneHelpers.AddSceneObject(m_scene, "inWorldObj", ua1.PrincipalID);
TaskInventoryItem scriptItem = TaskInventoryHelpers.AddScript(m_scene, inWorldObj.RootPart); 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(); 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 // 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); 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.Framework.Scenes;
using OpenSim.Region.ScriptEngine.Shared; using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api; using OpenSim.Region.ScriptEngine.Shared.Api;
using OpenSim.Region.ScriptEngine.Shared.Instance;
using OpenSim.Region.ScriptEngine.Shared.ScriptBase; using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
using OpenSim.Services.Interfaces; using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common; using OpenSim.Tests.Common;
@ -99,7 +100,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so); m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api(); OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null); osslApi.Initialize(m_engine, part, null, null);
string notecardName = "appearanceNc"; string notecardName = "appearanceNc";
osslApi.osOwnerSaveAppearance(notecardName); osslApi.osOwnerSaveAppearance(notecardName);
@ -125,7 +126,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so); m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api(); OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, so.RootPart, null); osslApi.Initialize(m_engine, so.RootPart, null, null);
bool gotExpectedException = false; bool gotExpectedException = false;
try try
@ -160,7 +161,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so); m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api(); OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null); osslApi.Initialize(m_engine, part, null, null);
string notecardName = "appearanceNc"; string notecardName = "appearanceNc";
osslApi.osOwnerSaveAppearance(notecardName); osslApi.osOwnerSaveAppearance(notecardName);
@ -194,7 +195,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so); m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api(); OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null); osslApi.Initialize(m_engine, part, null, null);
osslApi.osOwnerSaveAppearance(firstAppearanceNcName); osslApi.osOwnerSaveAppearance(firstAppearanceNcName);
@ -232,7 +233,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so); m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api(); OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null); osslApi.Initialize(m_engine, part, null, null);
osslApi.osOwnerSaveAppearance(firstAppearanceNcName); osslApi.osOwnerSaveAppearance(firstAppearanceNcName);
@ -284,10 +285,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(otherSo); m_scene.AddSceneObject(otherSo);
OSSL_Api osslApi = new OSSL_Api(); 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(); OSSL_Api otherOsslApi = new OSSL_Api();
otherOsslApi.Initialize(m_engine, otherPart, null); otherOsslApi.Initialize(m_engine, otherPart, null, null);
string notecardName = "appearanceNc"; string notecardName = "appearanceNc";
osslApi.osOwnerSaveAppearance(notecardName); osslApi.osOwnerSaveAppearance(notecardName);
@ -331,7 +332,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
m_scene.AddSceneObject(so); m_scene.AddSceneObject(so);
OSSL_Api osslApi = new OSSL_Api(); OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, part, null); osslApi.Initialize(m_engine, part, null, null);
string notecardName = "appearanceNc"; string notecardName = "appearanceNc";
osslApi.osOwnerSaveAppearance(notecardName); osslApi.osOwnerSaveAppearance(notecardName);

View File

@ -1284,11 +1284,11 @@ namespace OpenSim.Region.ScriptEngine.XEngine
m_DomainScripts[appDomain].Add(itemID); m_DomainScripts[appDomain].Add(itemID);
instance = new ScriptInstance(this, part, instance = new ScriptInstance(this, part,
itemID, assetID, assembly, item,
m_AppDomains[appDomain], startParam, postOnRez,
part.ParentGroup.RootPart.Name, m_MaxScriptQueue);
item.Name, startParam, postOnRez,
stateSource, m_MaxScriptQueue); instance.Load(m_AppDomains[appDomain], assembly, stateSource);
// if (DebugLevel >= 1) // if (DebugLevel >= 1)
// m_log.DebugFormat( // m_log.DebugFormat(
@ -1716,9 +1716,14 @@ namespace OpenSim.Region.ScriptEngine.XEngine
IScriptInstance instance = GetInstance(itemID); IScriptInstance instance = GetInstance(itemID);
if (instance != null) if (instance != null)
{
instance.Stop(m_WaitForEventCompletionOnScriptStop); instance.Stop(m_WaitForEventCompletionOnScriptStop);
}
else 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); m_runFlags.AddOrUpdate(itemID, false, 240);
}
} }
public DetectParams GetDetectParams(UUID itemID, int idx) 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 == == Running Tests ==

View File

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

View File

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