Merge branch 'master' into connector_plugin

connector_plugin
BlueWall 2012-10-20 05:25:58 -04:00
commit 07f1d44174
66 changed files with 3037 additions and 1405 deletions

View File

@ -223,9 +223,9 @@ namespace OpenSim.Capabilities.Handlers
// sending back the last byte instead of an error status // sending back the last byte instead of an error status
if (start >= texture.Data.Length) if (start >= texture.Data.Length)
{ {
m_log.DebugFormat( // m_log.DebugFormat(
"[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}", // "[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}",
texture.ID, start, texture.Data.Length); // texture.ID, start, texture.Data.Length);
// Stricly speaking, as per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, we should be sending back // Stricly speaking, as per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, we should be sending back
// Requested Range Not Satisfiable (416) here. However, it appears that at least recent implementations // Requested Range Not Satisfiable (416) here. However, it appears that at least recent implementations

View File

@ -34,7 +34,7 @@ using OpenMetaverse;
public class ConsoleUtil public class ConsoleUtil
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public const string MinRawConsoleVectorValue = "-~"; public const string MinRawConsoleVectorValue = "-~";
public const string MaxRawConsoleVectorValue = "~"; public const string MaxRawConsoleVectorValue = "~";
@ -107,7 +107,7 @@ public class ConsoleUtil
string semiDigestedConsoleVector = string.Join(VectorSeparator, semiDigestedComponents.ToArray()); string semiDigestedConsoleVector = string.Join(VectorSeparator, semiDigestedComponents.ToArray());
m_log.DebugFormat("[CONSOLE UTIL]: Parsing {0} into OpenMetaverse.Vector3", semiDigestedConsoleVector); // m_log.DebugFormat("[CONSOLE UTIL]: Parsing {0} into OpenMetaverse.Vector3", semiDigestedConsoleVector);
return Vector3.TryParse(semiDigestedConsoleVector, out vector); return Vector3.TryParse(semiDigestedConsoleVector, out vector);
} }

View File

@ -805,8 +805,23 @@ namespace OpenSim.Framework
event Action<IClientAPI> OnRegionHandShakeReply; event Action<IClientAPI> OnRegionHandShakeReply;
event GenericCall1 OnRequestWearables; event GenericCall1 OnRequestWearables;
event Action<IClientAPI, bool> OnCompleteMovementToRegion; event Action<IClientAPI, bool> OnCompleteMovementToRegion;
/// <summary>
/// Called when an AgentUpdate message is received and before OnAgentUpdate.
/// </summary>
/// <remarks>
/// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates.
/// </remarks>
event UpdateAgent OnPreAgentUpdate; event UpdateAgent OnPreAgentUpdate;
/// <summary>
/// Called when an AgentUpdate message is received and after OnPreAgentUpdate.
/// </summary>
/// <remarks>
/// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates.
/// </remarks>
event UpdateAgent OnAgentUpdate; event UpdateAgent OnAgentUpdate;
event AgentRequestSit OnAgentRequestSit; event AgentRequestSit OnAgentRequestSit;
event AgentSit OnAgentSit; event AgentSit OnAgentSit;
event AvatarPickerRequest OnAvatarPickerRequest; event AvatarPickerRequest OnAvatarPickerRequest;

View File

@ -49,8 +49,8 @@ namespace OpenSim.Framework
// use only one serializer to give the runtime a chance to // use only one serializer to give the runtime a chance to
// optimize it (it won't do that if you use a new instance // optimize it (it won't do that if you use a new instance
// every time) // every time)
private static XmlSerializer serializer = new XmlSerializer(typeof (LandData)); private static XmlSerializer serializer = new XmlSerializer(typeof(LandData));
private Vector3 _AABBMax = new Vector3(); private Vector3 _AABBMax = new Vector3();
private Vector3 _AABBMin = new Vector3(); private Vector3 _AABBMin = new Vector3();
private int _area = 0; private int _area = 0;
@ -65,11 +65,11 @@ namespace OpenSim.Framework
private byte[] _bitmap = new byte[512]; private byte[] _bitmap = new byte[512];
private string _description = String.Empty; private string _description = String.Empty;
private uint _flags = (uint) ParcelFlags.AllowFly | (uint) ParcelFlags.AllowLandmark | private uint _flags = (uint)ParcelFlags.AllowFly | (uint)ParcelFlags.AllowLandmark |
(uint) ParcelFlags.AllowAPrimitiveEntry | (uint)ParcelFlags.AllowAPrimitiveEntry |
(uint) ParcelFlags.AllowDeedToGroup | (uint) ParcelFlags.AllowTerraform | (uint)ParcelFlags.AllowDeedToGroup | (uint)ParcelFlags.AllowTerraform |
(uint) ParcelFlags.CreateObjects | (uint) ParcelFlags.AllowOtherScripts | (uint)ParcelFlags.CreateObjects | (uint)ParcelFlags.AllowOtherScripts |
(uint) ParcelFlags.SoundLocal | (uint) ParcelFlags.AllowVoiceChat; (uint)ParcelFlags.SoundLocal | (uint)ParcelFlags.AllowVoiceChat;
private byte _landingType = 0; private byte _landingType = 0;
private string _name = "Your Parcel"; private string _name = "Your Parcel";
@ -97,16 +97,36 @@ namespace OpenSim.Framework
private bool _mediaLoop = false; private bool _mediaLoop = false;
private bool _obscureMusic = false; private bool _obscureMusic = false;
private bool _obscureMedia = false; private bool _obscureMedia = false;
private float _dwell = 0;
/// <summary>
/// Traffic count of parcel
/// </summary>
[XmlIgnore]
public float Dwell
{
get
{
return _dwell;
}
set
{
_dwell = value;
}
}
/// <summary> /// <summary>
/// Whether to obscure parcel media URL /// Whether to obscure parcel media URL
/// </summary> /// </summary>
[XmlIgnore] [XmlIgnore]
public bool ObscureMedia { public bool ObscureMedia
get { {
get
{
return _obscureMedia; return _obscureMedia;
} }
set { set
{
_obscureMedia = value; _obscureMedia = value;
} }
} }
@ -115,11 +135,14 @@ namespace OpenSim.Framework
/// Whether to obscure parcel music URL /// Whether to obscure parcel music URL
/// </summary> /// </summary>
[XmlIgnore] [XmlIgnore]
public bool ObscureMusic { public bool ObscureMusic
get { {
get
{
return _obscureMusic; return _obscureMusic;
} }
set { set
{
_obscureMusic = value; _obscureMusic = value;
} }
} }
@ -128,11 +151,14 @@ namespace OpenSim.Framework
/// Whether to loop parcel media /// Whether to loop parcel media
/// </summary> /// </summary>
[XmlIgnore] [XmlIgnore]
public bool MediaLoop { public bool MediaLoop
get { {
get
{
return _mediaLoop; return _mediaLoop;
} }
set { set
{
_mediaLoop = value; _mediaLoop = value;
} }
} }
@ -141,11 +167,14 @@ namespace OpenSim.Framework
/// Height of parcel media render /// Height of parcel media render
/// </summary> /// </summary>
[XmlIgnore] [XmlIgnore]
public int MediaHeight { public int MediaHeight
get { {
get
{
return _mediaHeight; return _mediaHeight;
} }
set { set
{
_mediaHeight = value; _mediaHeight = value;
} }
} }
@ -154,11 +183,14 @@ namespace OpenSim.Framework
/// Width of parcel media render /// Width of parcel media render
/// </summary> /// </summary>
[XmlIgnore] [XmlIgnore]
public int MediaWidth { public int MediaWidth
get { {
get
{
return _mediaWidth; return _mediaWidth;
} }
set { set
{
_mediaWidth = value; _mediaWidth = value;
} }
} }
@ -167,11 +199,14 @@ namespace OpenSim.Framework
/// Upper corner of the AABB for the parcel /// Upper corner of the AABB for the parcel
/// </summary> /// </summary>
[XmlIgnore] [XmlIgnore]
public Vector3 AABBMax { public Vector3 AABBMax
get { {
get
{
return _AABBMax; return _AABBMax;
} }
set { set
{
_AABBMax = value; _AABBMax = value;
} }
} }
@ -179,11 +214,14 @@ namespace OpenSim.Framework
/// Lower corner of the AABB for the parcel /// Lower corner of the AABB for the parcel
/// </summary> /// </summary>
[XmlIgnore] [XmlIgnore]
public Vector3 AABBMin { public Vector3 AABBMin
get { {
get
{
return _AABBMin; return _AABBMin;
} }
set { set
{
_AABBMin = value; _AABBMin = value;
} }
} }
@ -191,11 +229,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Area in meters^2 the parcel contains /// Area in meters^2 the parcel contains
/// </summary> /// </summary>
public int Area { public int Area
get { {
get
{
return _area; return _area;
} }
set { set
{
_area = value; _area = value;
} }
} }
@ -203,11 +244,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// ID of auction (3rd Party Integration) when parcel is being auctioned /// ID of auction (3rd Party Integration) when parcel is being auctioned
/// </summary> /// </summary>
public uint AuctionID { public uint AuctionID
get { {
get
{
return _auctionID; return _auctionID;
} }
set { set
{
_auctionID = value; _auctionID = value;
} }
} }
@ -215,11 +259,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// UUID of authorized buyer of parcel. This is UUID.Zero if anyone can buy it. /// UUID of authorized buyer of parcel. This is UUID.Zero if anyone can buy it.
/// </summary> /// </summary>
public UUID AuthBuyerID { public UUID AuthBuyerID
get { {
get
{
return _authBuyerID; return _authBuyerID;
} }
set { set
{
_authBuyerID = value; _authBuyerID = value;
} }
} }
@ -227,11 +274,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Category of parcel. Used for classifying the parcel in classified listings /// Category of parcel. Used for classifying the parcel in classified listings
/// </summary> /// </summary>
public ParcelCategory Category { public ParcelCategory Category
get { {
get
{
return _category; return _category;
} }
set { set
{
_category = value; _category = value;
} }
} }
@ -239,11 +289,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Date that the current owner purchased or claimed the parcel /// Date that the current owner purchased or claimed the parcel
/// </summary> /// </summary>
public int ClaimDate { public int ClaimDate
get { {
get
{
return _claimDate; return _claimDate;
} }
set { set
{
_claimDate = value; _claimDate = value;
} }
} }
@ -251,11 +304,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// The last price that the parcel was sold at /// The last price that the parcel was sold at
/// </summary> /// </summary>
public int ClaimPrice { public int ClaimPrice
get { {
get
{
return _claimPrice; return _claimPrice;
} }
set { set
{
_claimPrice = value; _claimPrice = value;
} }
} }
@ -263,11 +319,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Global ID for the parcel. (3rd Party Integration) /// Global ID for the parcel. (3rd Party Integration)
/// </summary> /// </summary>
public UUID GlobalID { public UUID GlobalID
get { {
get
{
return _globalID; return _globalID;
} }
set { set
{
_globalID = value; _globalID = value;
} }
} }
@ -275,11 +334,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Unique ID of the Group that owns /// Unique ID of the Group that owns
/// </summary> /// </summary>
public UUID GroupID { public UUID GroupID
get { {
get
{
return _groupID; return _groupID;
} }
set { set
{
_groupID = value; _groupID = value;
} }
} }
@ -287,11 +349,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Returns true if the Land Parcel is owned by a group /// Returns true if the Land Parcel is owned by a group
/// </summary> /// </summary>
public bool IsGroupOwned { public bool IsGroupOwned
get { {
get
{
return _isGroupOwned; return _isGroupOwned;
} }
set { set
{
_isGroupOwned = value; _isGroupOwned = value;
} }
} }
@ -299,11 +364,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// jp2 data for the image representative of the parcel in the parcel dialog /// jp2 data for the image representative of the parcel in the parcel dialog
/// </summary> /// </summary>
public byte[] Bitmap { public byte[] Bitmap
get { {
get
{
return _bitmap; return _bitmap;
} }
set { set
{
_bitmap = value; _bitmap = value;
} }
} }
@ -311,11 +379,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Parcel Description /// Parcel Description
/// </summary> /// </summary>
public string Description { public string Description
get { {
get
{
return _description; return _description;
} }
set { set
{
_description = value; _description = value;
} }
} }
@ -323,11 +394,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Parcel settings. Access flags, Fly, NoPush, Voice, Scripts allowed, etc. ParcelFlags /// Parcel settings. Access flags, Fly, NoPush, Voice, Scripts allowed, etc. ParcelFlags
/// </summary> /// </summary>
public uint Flags { public uint Flags
get { {
get
{
return _flags; return _flags;
} }
set { set
{
_flags = value; _flags = value;
} }
} }
@ -336,11 +410,14 @@ namespace OpenSim.Framework
/// Determines if people are able to teleport where they please on the parcel or if they /// Determines if people are able to teleport where they please on the parcel or if they
/// get constrainted to a specific point on teleport within the parcel /// get constrainted to a specific point on teleport within the parcel
/// </summary> /// </summary>
public byte LandingType { public byte LandingType
get { {
get
{
return _landingType; return _landingType;
} }
set { set
{
_landingType = value; _landingType = value;
} }
} }
@ -348,11 +425,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Parcel Name /// Parcel Name
/// </summary> /// </summary>
public string Name { public string Name
get { {
get
{
return _name; return _name;
} }
set { set
{
_name = value; _name = value;
} }
} }
@ -360,11 +440,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Status of Parcel, Leased, Abandoned, For Sale /// Status of Parcel, Leased, Abandoned, For Sale
/// </summary> /// </summary>
public ParcelStatus Status { public ParcelStatus Status
get { {
get
{
return _status; return _status;
} }
set { set
{
_status = value; _status = value;
} }
} }
@ -372,11 +455,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Internal ID of the parcel. Sometimes the client will try to use this value /// Internal ID of the parcel. Sometimes the client will try to use this value
/// </summary> /// </summary>
public int LocalID { public int LocalID
get { {
get
{
return _localID; return _localID;
} }
set { set
{
_localID = value; _localID = value;
} }
} }
@ -384,11 +470,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Determines if we scale the media based on the surface it's on /// Determines if we scale the media based on the surface it's on
/// </summary> /// </summary>
public byte MediaAutoScale { public byte MediaAutoScale
get { {
get
{
return _mediaAutoScale; return _mediaAutoScale;
} }
set { set
{
_mediaAutoScale = value; _mediaAutoScale = value;
} }
} }
@ -396,11 +485,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Texture Guid to replace with the output of the media stream /// Texture Guid to replace with the output of the media stream
/// </summary> /// </summary>
public UUID MediaID { public UUID MediaID
get { {
get
{
return _mediaID; return _mediaID;
} }
set { set
{
_mediaID = value; _mediaID = value;
} }
} }
@ -408,11 +500,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// URL to the media file to display /// URL to the media file to display
/// </summary> /// </summary>
public string MediaURL { public string MediaURL
get { {
get
{
return _mediaURL; return _mediaURL;
} }
set { set
{
_mediaURL = value; _mediaURL = value;
} }
} }
@ -432,11 +527,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// URL to the shoutcast music stream to play on the parcel /// URL to the shoutcast music stream to play on the parcel
/// </summary> /// </summary>
public string MusicURL { public string MusicURL
get { {
get
{
return _musicURL; return _musicURL;
} }
set { set
{
_musicURL = value; _musicURL = value;
} }
} }
@ -445,11 +543,14 @@ namespace OpenSim.Framework
/// Owner Avatar or Group of the parcel. Naturally, all land masses must be /// Owner Avatar or Group of the parcel. Naturally, all land masses must be
/// owned by someone /// owned by someone
/// </summary> /// </summary>
public UUID OwnerID { public UUID OwnerID
get { {
get
{
return _ownerID; return _ownerID;
} }
set { set
{
_ownerID = value; _ownerID = value;
} }
} }
@ -457,11 +558,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// List of access data for the parcel. User data, some bitflags, and a time /// List of access data for the parcel. User data, some bitflags, and a time
/// </summary> /// </summary>
public List<LandAccessEntry> ParcelAccessList { public List<LandAccessEntry> ParcelAccessList
get { {
get
{
return _parcelAccessList; return _parcelAccessList;
} }
set { set
{
_parcelAccessList = value; _parcelAccessList = value;
} }
} }
@ -469,11 +573,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// How long in hours a Pass to the parcel is given /// How long in hours a Pass to the parcel is given
/// </summary> /// </summary>
public float PassHours { public float PassHours
get { {
get
{
return _passHours; return _passHours;
} }
set { set
{
_passHours = value; _passHours = value;
} }
} }
@ -481,11 +588,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// Price to purchase a Pass to a restricted parcel /// Price to purchase a Pass to a restricted parcel
/// </summary> /// </summary>
public int PassPrice { public int PassPrice
get { {
get
{
return _passPrice; return _passPrice;
} }
set { set
{
_passPrice = value; _passPrice = value;
} }
} }
@ -493,11 +603,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// When the parcel is being sold, this is the price to purchase the parcel /// When the parcel is being sold, this is the price to purchase the parcel
/// </summary> /// </summary>
public int SalePrice { public int SalePrice
get { {
get
{
return _salePrice; return _salePrice;
} }
set { set
{
_salePrice = value; _salePrice = value;
} }
} }
@ -506,11 +619,14 @@ namespace OpenSim.Framework
/// Number of meters^2 in the Simulator /// Number of meters^2 in the Simulator
/// </summary> /// </summary>
[XmlIgnore] [XmlIgnore]
public int SimwideArea { public int SimwideArea
get { {
get
{
return _simwideArea; return _simwideArea;
} }
set { set
{
_simwideArea = value; _simwideArea = value;
} }
} }
@ -519,11 +635,14 @@ namespace OpenSim.Framework
/// Number of SceneObjectPart in the Simulator /// Number of SceneObjectPart in the Simulator
/// </summary> /// </summary>
[XmlIgnore] [XmlIgnore]
public int SimwidePrims { public int SimwidePrims
get { {
get
{
return _simwidePrims; return _simwidePrims;
} }
set { set
{
_simwidePrims = value; _simwidePrims = value;
} }
} }
@ -531,11 +650,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// ID of the snapshot used in the client parcel dialog of the parcel /// ID of the snapshot used in the client parcel dialog of the parcel
/// </summary> /// </summary>
public UUID SnapshotID { public UUID SnapshotID
get { {
get
{
return _snapshotID; return _snapshotID;
} }
set { set
{
_snapshotID = value; _snapshotID = value;
} }
} }
@ -544,11 +666,14 @@ namespace OpenSim.Framework
/// When teleporting is restricted to a certain point, this is the location /// When teleporting is restricted to a certain point, this is the location
/// that the user will be redirected to /// that the user will be redirected to
/// </summary> /// </summary>
public Vector3 UserLocation { public Vector3 UserLocation
get { {
get
{
return _userLocation; return _userLocation;
} }
set { set
{
_userLocation = value; _userLocation = value;
} }
} }
@ -557,11 +682,14 @@ namespace OpenSim.Framework
/// When teleporting is restricted to a certain point, this is the rotation /// When teleporting is restricted to a certain point, this is the rotation
/// that the user will be positioned /// that the user will be positioned
/// </summary> /// </summary>
public Vector3 UserLookAt { public Vector3 UserLookAt
get { {
get
{
return _userLookAt; return _userLookAt;
} }
set { set
{
_userLookAt = value; _userLookAt = value;
} }
} }
@ -570,11 +698,14 @@ namespace OpenSim.Framework
/// Autoreturn number of minutes to return SceneObjectGroup that are owned by someone who doesn't own /// Autoreturn number of minutes to return SceneObjectGroup that are owned by someone who doesn't own
/// the parcel and isn't set to the same 'group' as the parcel. /// the parcel and isn't set to the same 'group' as the parcel.
/// </summary> /// </summary>
public int OtherCleanTime { public int OtherCleanTime
get { {
get
{
return _otherCleanTime; return _otherCleanTime;
} }
set { set
{
_otherCleanTime = value; _otherCleanTime = value;
} }
} }
@ -582,11 +713,14 @@ namespace OpenSim.Framework
/// <summary> /// <summary>
/// parcel media description /// parcel media description
/// </summary> /// </summary>
public string MediaDescription { public string MediaDescription
get { {
get
{
return _mediaDescription; return _mediaDescription;
} }
set { set
{
_mediaDescription = value; _mediaDescription = value;
} }
} }
@ -622,7 +756,7 @@ namespace OpenSim.Framework
landData._mediaURL = _mediaURL; landData._mediaURL = _mediaURL;
landData._musicURL = _musicURL; landData._musicURL = _musicURL;
landData._ownerID = _ownerID; landData._ownerID = _ownerID;
landData._bitmap = (byte[]) _bitmap.Clone(); landData._bitmap = (byte[])_bitmap.Clone();
landData._description = _description; landData._description = _description;
landData._flags = _flags; landData._flags = _flags;
landData._name = _name; landData._name = _name;
@ -643,6 +777,7 @@ namespace OpenSim.Framework
landData._obscureMedia = _obscureMedia; landData._obscureMedia = _obscureMedia;
landData._simwideArea = _simwideArea; landData._simwideArea = _simwideArea;
landData._simwidePrims = _simwidePrims; landData._simwidePrims = _simwidePrims;
landData._dwell = _dwell;
landData._parcelAccessList.Clear(); landData._parcelAccessList.Clear();
foreach (LandAccessEntry entry in _parcelAccessList) foreach (LandAccessEntry entry in _parcelAccessList)

View File

@ -49,7 +49,11 @@ namespace OpenSim.Framework.Monitoring
Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)); Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
sb.AppendFormat( sb.AppendFormat(
"OpenSim object memory churn : {0} MB/s\n", "OpenSim last object memory churn : {0} MB/s\n",
Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3));
sb.AppendFormat(
"OpenSim average object memory churn : {0} MB/s\n",
Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3)); Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3));
sb.AppendFormat( sb.AppendFormat(

View File

@ -60,13 +60,21 @@ namespace OpenSim.Framework.Monitoring
private static bool m_enabled; private static bool m_enabled;
/// <summary> /// <summary>
/// Average memory churn in bytes per millisecond. /// Last memory churn in bytes per millisecond.
/// </summary> /// </summary>
public static double AverageMemoryChurn public static double AverageMemoryChurn
{ {
get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; } get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; }
} }
/// <summary>
/// Average memory churn in bytes per millisecond.
/// </summary>
public static double LastMemoryChurn
{
get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; }
}
/// <summary> /// <summary>
/// Maximum number of statistical samples. /// Maximum number of statistical samples.
/// </summary> /// </summary>

View File

@ -359,13 +359,19 @@ Asset service request failures: {3}" + Environment.NewLine,
inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime, inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime,
netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime)); netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime));
foreach (KeyValuePair<string, Stat> kvp in StatsManager.RegisteredStats) Dictionary<string, Dictionary<string, Stat>> sceneStats;
{
Stat stat = kvp.Value;
if (stat.Category == "scene" && stat.Verbosity == StatVerbosity.Info) if (StatsManager.TryGetStats("scene", out sceneStats))
{
foreach (KeyValuePair<string, Dictionary<string, Stat>> kvp in sceneStats)
{ {
sb.AppendFormat("Slow frames ({0}): {1}\n", stat.Container, stat.Value); foreach (Stat stat in kvp.Value.Values)
{
if (stat.Verbosity == StatVerbosity.Info)
{
sb.AppendFormat("{0} ({1}): {2}{3}\n", stat.Name, stat.Container, stat.Value, stat.UnitName);
}
}
} }
} }

View File

@ -35,13 +35,23 @@ namespace OpenSim.Framework.Monitoring
/// </summary> /// </summary>
public class StatsManager public class StatsManager
{ {
// Subcommand used to list other stats.
public const string AllSubCommand = "all";
// Subcommand used to list other stats.
public const string ListSubCommand = "list";
// All subcommands
public static HashSet<string> SubCommands = new HashSet<string> { AllSubCommand, ListSubCommand };
/// <summary> /// <summary>
/// Registered stats. /// Registered stats categorized by category/container/shortname
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Do not add or remove from this dictionary. /// Do not add or remove directly from this dictionary.
/// </remarks> /// </remarks>
public static Dictionary<string, Stat> RegisteredStats = new Dictionary<string, Stat>(); public static Dictionary<string, Dictionary<string, Dictionary<string, Stat>>> RegisteredStats
= new Dictionary<string, Dictionary<string, Dictionary<string, Stat>>>();
private static AssetStatsCollector assetStats; private static AssetStatsCollector assetStats;
private static UserStatsCollector userStats; private static UserStatsCollector userStats;
@ -51,6 +61,75 @@ namespace OpenSim.Framework.Monitoring
public static UserStatsCollector UserStats { get { return userStats; } } public static UserStatsCollector UserStats { get { return userStats; } }
public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } }
public static void RegisterConsoleCommands(ICommandConsole console)
{
console.Commands.AddCommand(
"General",
false,
"show stats",
"show stats [list|all|<category>]",
"Show statistical information for this server",
"If no final argument is specified then legacy statistics information is currently shown.\n"
+ "If list is specified then statistic categories are shown.\n"
+ "If all is specified then all registered statistics are shown.\n"
+ "If a category name is specified then only statistics from that category are shown.\n"
+ "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS",
HandleShowStatsCommand);
}
public static void HandleShowStatsCommand(string module, string[] cmd)
{
ICommandConsole con = MainConsole.Instance;
if (cmd.Length > 2)
{
var categoryName = cmd[2];
if (categoryName == AllSubCommand)
{
foreach (var category in RegisteredStats.Values)
{
OutputCategoryStatsToConsole(con, category);
}
}
else if (categoryName == ListSubCommand)
{
con.Output("Statistic categories available are:");
foreach (string category in RegisteredStats.Keys)
con.OutputFormat(" {0}", category);
}
else
{
Dictionary<string, Dictionary<string, Stat>> category;
if (!RegisteredStats.TryGetValue(categoryName, out category))
{
con.OutputFormat("No such category as {0}", categoryName);
}
else
{
OutputCategoryStatsToConsole(con, category);
}
}
}
else
{
// Legacy
con.Output(SimExtraStats.Report());
}
}
private static void OutputCategoryStatsToConsole(
ICommandConsole con, Dictionary<string, Dictionary<string, Stat>> category)
{
foreach (var container in category.Values)
{
foreach (Stat stat in container.Values)
{
con.Output(stat.ToConsoleString());
}
}
}
/// <summary> /// <summary>
/// Start collecting statistics related to assets. /// Start collecting statistics related to assets.
/// Should only be called once. /// Should only be called once.
@ -73,43 +152,100 @@ namespace OpenSim.Framework.Monitoring
return userStats; return userStats;
} }
/// <summary>
/// Registers a statistic.
/// </summary>
/// <param name='stat'></param>
/// <returns></returns>
public static bool RegisterStat(Stat stat) public static bool RegisterStat(Stat stat)
{ {
Dictionary<string, Dictionary<string, Stat>> category = null, newCategory;
Dictionary<string, Stat> container = null, newContainer;
lock (RegisteredStats) lock (RegisteredStats)
{ {
if (RegisteredStats.ContainsKey(stat.UniqueName)) // Stat name is not unique across category/container/shortname key.
{ // XXX: For now just return false. This is to avoid problems in regression tests where all tests
// XXX: For now just return false. This is to avoid problems in regression tests where all tests // in a class are run in the same instance of the VM.
// in a class are run in the same instance of the VM. if (TryGetStat(stat, out category, out container))
return false; return false;
// throw new Exception( // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed.
// "StatsManager already contains stat with ShortName {0} in Category {1}", stat.ShortName, stat.Category); // This means that we don't need to lock or copy them on iteration, which will be a much more
} // common operation after startup.
if (container != null)
newContainer = new Dictionary<string, Stat>(container);
else
newContainer = new Dictionary<string, Stat>();
// We take a replace-on-write approach here so that we don't need to generate a new Dictionary if (category != null)
Dictionary<string, Stat> newRegisteredStats = new Dictionary<string, Stat>(RegisteredStats); newCategory = new Dictionary<string, Dictionary<string, Stat>>(category);
newRegisteredStats[stat.UniqueName] = stat; else
RegisteredStats = newRegisteredStats; newCategory = new Dictionary<string, Dictionary<string, Stat>>();
newContainer[stat.ShortName] = stat;
newCategory[stat.Container] = newContainer;
RegisteredStats[stat.Category] = newCategory;
} }
return true; return true;
} }
/// <summary>
/// Deregister a statistic
/// </summary>>
/// <param name='stat'></param>
/// <returns></returns
public static bool DeregisterStat(Stat stat) public static bool DeregisterStat(Stat stat)
{ {
Dictionary<string, Dictionary<string, Stat>> category = null, newCategory;
Dictionary<string, Stat> container = null, newContainer;
lock (RegisteredStats) lock (RegisteredStats)
{ {
if (!RegisteredStats.ContainsKey(stat.UniqueName)) if (!TryGetStat(stat, out category, out container))
return false; return false;
Dictionary<string, Stat> newRegisteredStats = new Dictionary<string, Stat>(RegisteredStats); newContainer = new Dictionary<string, Stat>(container);
newRegisteredStats.Remove(stat.UniqueName); newContainer.Remove(stat.UniqueName);
RegisteredStats = newRegisteredStats;
newCategory = new Dictionary<string, Dictionary<string, Stat>>(category);
newCategory.Remove(stat.Container);
newCategory[stat.Container] = newContainer;
RegisteredStats[stat.Category] = newCategory;
return true; return true;
} }
} }
public static bool TryGetStats(string category, out Dictionary<string, Dictionary<string, Stat>> stats)
{
return RegisteredStats.TryGetValue(category, out stats);
}
public static bool TryGetStat(
Stat stat,
out Dictionary<string, Dictionary<string, Stat>> category,
out Dictionary<string, Stat> container)
{
category = null;
container = null;
lock (RegisteredStats)
{
if (RegisteredStats.TryGetValue(stat.Category, out category))
{
if (category.TryGetValue(stat.Container, out container))
{
if (container.ContainsKey(stat.ShortName))
return true;
}
}
}
return false;
}
} }
/// <summary> /// <summary>
@ -157,9 +293,26 @@ namespace OpenSim.Framework.Monitoring
public virtual double Value { get; set; } public virtual double Value { get; set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name='shortName'>Short name for the stat. Must not contain spaces. e.g. "LongFrames"</param>
/// <param name='name'>Human readable name for the stat. e.g. "Long frames"</param>
/// <param name='unitName'>
/// Unit name for the stat. Should be preceeded by a space if the unit name isn't normally appeneded immediately to the value.
/// e.g. " frames"
/// </param>
/// <param name='category'>Category under which this stat should appear, e.g. "scene". Do not capitalize.</param>
/// <param name='container'>Entity to which this stat relates. e.g. scene name if this is a per scene stat.</param>
/// <param name='verbosity'>Verbosity of stat. Controls whether it will appear in short stat display or only full display.</param>
/// <param name='description'>Description of stat</param>
public Stat( public Stat(
string shortName, string name, string unitName, string category, string container, StatVerbosity verbosity, string description) string shortName, string name, string unitName, string category, string container, StatVerbosity verbosity, string description)
{ {
if (StatsManager.SubCommands.Contains(category))
throw new Exception(
string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category));
ShortName = shortName; ShortName = shortName;
Name = name; Name = name;
UnitName = unitName; UnitName = unitName;
@ -175,6 +328,12 @@ namespace OpenSim.Framework.Monitoring
{ {
return string.Format("{0}+{1}+{2}", container, category, shortName); return string.Format("{0}+{1}+{2}", container, category, shortName);
} }
public virtual string ToConsoleString()
{
return string.Format(
"{0}.{1}.{2} : {3}{4}", Category, Container, ShortName, Value, UnitName);
}
} }
public class PercentageStat : Stat public class PercentageStat : Stat
@ -192,7 +351,7 @@ namespace OpenSim.Framework.Monitoring
if (c == 0) if (c == 0)
return 0; return 0;
return (double)Antecedent / c; return (double)Antecedent / c * 100;
} }
set set
@ -203,8 +362,13 @@ namespace OpenSim.Framework.Monitoring
public PercentageStat( public PercentageStat(
string shortName, string name, string category, string container, StatVerbosity verbosity, string description) string shortName, string name, string category, string container, StatVerbosity verbosity, string description)
: base(shortName, name, " %", category, container, verbosity, description) : base(shortName, name, "%", category, container, verbosity, description) {}
public override string ToConsoleString()
{ {
return string.Format(
"{0}.{1}.{2} : {3:0.##}{4} ({5}/{6})",
Category, Container, ShortName, Value, UnitName, Antecedent, Consequent);
} }
} }
} }

View File

@ -231,7 +231,25 @@ namespace OpenSim.Framework.Monitoring
private static bool RemoveThread(int threadID) private static bool RemoveThread(int threadID)
{ {
lock (m_threads) lock (m_threads)
return m_threads.Remove(threadID); {
ThreadWatchdogInfo twi;
if (m_threads.TryGetValue(threadID, out twi))
{
m_log.DebugFormat(
"[WATCHDOG]: Removing thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId);
m_threads.Remove(threadID);
return true;
}
else
{
m_log.WarnFormat(
"[WATCHDOG]: Requested to remove thread with ID {0} but this is not being monitored", threadID);
return false;
}
}
} }
public static bool AbortThread(int threadID) public static bool AbortThread(int threadID)

76
OpenSim/Framework/Pool.cs Normal file
View File

@ -0,0 +1,76 @@
/*
* 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;
namespace OpenSim.Framework
{
/// <summary>
/// Naive pool implementation.
/// </summary>
/// <remarks>
/// Currently assumes that objects are in a useable state when returned.
/// </remarks>
public class Pool<T>
{
private Stack<T> m_pool;
private int m_maxPoolSize;
private Func<T> m_createFunction;
public Pool(Func<T> createFunction, int maxSize)
{
m_maxPoolSize = maxSize;
m_createFunction = createFunction;
m_pool = new Stack<T>(m_maxPoolSize);
}
public T GetObject()
{
lock (m_pool)
{
if (m_pool.Count > 0)
return m_pool.Pop();
else
return m_createFunction();
}
}
public void ReturnObject(T obj)
{
lock (m_pool)
{
if (m_pool.Count >= m_maxPoolSize)
return;
else
m_pool.Push(obj);
}
}
}
}

View File

@ -96,11 +96,6 @@ namespace OpenSim.Framework.Servers
get { return m_httpServer; } get { return m_httpServer; }
} }
/// <summary>
/// Holds the non-viewer statistics collection object for this service/server
/// </summary>
protected IStatsCollector m_stats;
public BaseOpenSimServer() public BaseOpenSimServer()
{ {
m_startuptime = DateTime.Now; m_startuptime = DateTime.Now;
@ -177,10 +172,6 @@ namespace OpenSim.Framework.Servers
"show info", "show info",
"Show general information about the server", HandleShow); "Show general information about the server", HandleShow);
m_console.Commands.AddCommand("General", false, "show stats",
"show stats",
"Show statistics", HandleShow);
m_console.Commands.AddCommand("General", false, "show threads", m_console.Commands.AddCommand("General", false, "show threads",
"show threads", "show threads",
"Show thread status", HandleShow); "Show thread status", HandleShow);
@ -226,12 +217,7 @@ namespace OpenSim.Framework.Servers
{ {
StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n"); StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n");
sb.Append(GetUptimeReport()); sb.Append(GetUptimeReport());
sb.Append(StatsManager.SimExtraStats.Report());
if (m_stats != null)
{
sb.Append(m_stats.Report());
}
sb.Append(Environment.NewLine); sb.Append(Environment.NewLine);
sb.Append(GetThreadsReport()); sb.Append(GetThreadsReport());
@ -382,10 +368,6 @@ namespace OpenSim.Framework.Servers
{ {
Notice("set log level [level] - change the console logging level only. For example, off or debug."); Notice("set log level [level] - change the console logging level only. For example, off or debug.");
Notice("show info - show server information (e.g. startup path)."); Notice("show info - show server information (e.g. startup path).");
if (m_stats != null)
Notice("show stats - show statistical information for this server");
Notice("show threads - list tracked threads"); Notice("show threads - list tracked threads");
Notice("show uptime - show server startup time and uptime."); Notice("show uptime - show server startup time and uptime.");
Notice("show version - show server version."); Notice("show version - show server version.");
@ -409,11 +391,6 @@ namespace OpenSim.Framework.Servers
ShowInfo(); ShowInfo();
break; break;
case "stats":
if (m_stats != null)
Notice(m_stats.Report());
break;
case "threads": case "threads":
Notice(GetThreadsReport()); Notice(GetThreadsReport());
break; break;
@ -604,8 +581,7 @@ namespace OpenSim.Framework.Servers
public string osSecret { public string osSecret {
// Secret uuid for the simulator // Secret uuid for the simulator
get { return m_osSecret; } get { return m_osSecret; }
} }
public string StatReport(IOSHttpRequest httpRequest) public string StatReport(IOSHttpRequest httpRequest)
@ -613,11 +589,11 @@ namespace OpenSim.Framework.Servers
// If we catch a request for "callback", wrap the response in the value for jsonp // If we catch a request for "callback", wrap the response in the value for jsonp
if (httpRequest.Query.ContainsKey("callback")) if (httpRequest.Query.ContainsKey("callback"))
{ {
return httpRequest.Query["callback"].ToString() + "(" + m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");"; return httpRequest.Query["callback"].ToString() + "(" + StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");";
} }
else else
{ {
return m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version); return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
} }
} }

View File

@ -648,7 +648,7 @@ namespace OpenSim.Framework.Servers.HttpServer
// Every month or so this will wrap and give bad numbers, not really a problem // Every month or so this will wrap and give bad numbers, not really a problem
// since its just for reporting // since its just for reporting
int tickdiff = requestEndTick - requestStartTick; int tickdiff = requestEndTick - requestStartTick;
if (tickdiff > 3000 && requestHandler.Name != "GetTexture") if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture")
{ {
m_log.InfoFormat( m_log.InfoFormat(
"[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms", "[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms",

View File

@ -254,8 +254,14 @@ namespace OpenSim
m_console.Commands.AddCommand("Debug", false, "debug teleport", "debug teleport", "Toggle teleport route debugging", Debug); m_console.Commands.AddCommand("Debug", false, "debug teleport", "debug teleport", "Toggle teleport route debugging", Debug);
m_console.Commands.AddCommand("Debug", false, "debug scene", m_console.Commands.AddCommand("Debug", false, "debug scene",
"debug scene <scripting> <collisions> <physics>", "debug scene active|collisions|physics|scripting|teleport true|false",
"Turn on scene debugging", Debug); "Turn on scene debugging.",
"If active is false then main scene update and maintenance loops are suspended.\n"
+ "If collisions is false then collisions with other objects are turned off.\n"
+ "If physics is false then all physics objects are non-physical.\n"
+ "If scripting is false then no scripting operations happen.\n"
+ "If teleport is true then some extra teleport debug information is logged.",
Debug);
m_console.Commands.AddCommand("General", false, "change region", m_console.Commands.AddCommand("General", false, "change region",
"change region <region name>", "change region <region name>",
@ -930,7 +936,8 @@ namespace OpenSim
} }
else else
{ {
MainConsole.Instance.Output("Usage: debug scene scripting|collisions|physics|teleport true|false"); MainConsole.Instance.Output(
"Usage: debug scene active|scripting|collisions|physics|teleport true|false");
} }
break; break;

View File

@ -223,8 +223,6 @@ namespace OpenSim
base.StartupSpecific(); base.StartupSpecific();
m_stats = StatsManager.SimExtraStats;
// Create a ModuleLoader instance // Create a ModuleLoader instance
m_moduleLoader = new ModuleLoader(m_config.Source); m_moduleLoader = new ModuleLoader(m_config.Source);
@ -234,51 +232,51 @@ namespace OpenSim
plugin.PostInitialise(); plugin.PostInitialise();
} }
AddPluginCommands();
}
protected virtual void AddPluginCommands()
{
// If console exists add plugin commands.
if (m_console != null) if (m_console != null)
{ {
List<string> topics = GetHelpTopics(); StatsManager.RegisterConsoleCommands(m_console);
AddPluginCommands(m_console);
}
}
foreach (string topic in topics) protected virtual void AddPluginCommands(CommandConsole console)
{
List<string> topics = GetHelpTopics();
foreach (string topic in topics)
{
string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1);
// This is a hack to allow the user to enter the help command in upper or lowercase. This will go
// away at some point.
console.Commands.AddCommand(capitalizedTopic, false, "help " + topic,
"help " + capitalizedTopic,
"Get help on plugin command '" + topic + "'",
HandleCommanderHelp);
console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic,
"help " + capitalizedTopic,
"Get help on plugin command '" + topic + "'",
HandleCommanderHelp);
ICommander commander = null;
Scene s = SceneManager.CurrentOrFirstScene;
if (s != null && s.GetCommanders() != null)
{ {
string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1); if (s.GetCommanders().ContainsKey(topic))
commander = s.GetCommanders()[topic];
}
// This is a hack to allow the user to enter the help command in upper or lowercase. This will go if (commander == null)
// away at some point. continue;
m_console.Commands.AddCommand(capitalizedTopic, false, "help " + topic,
"help " + capitalizedTopic,
"Get help on plugin command '" + topic + "'",
HandleCommanderHelp);
m_console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic,
"help " + capitalizedTopic,
"Get help on plugin command '" + topic + "'",
HandleCommanderHelp);
ICommander commander = null; foreach (string command in commander.Commands.Keys)
{
Scene s = SceneManager.CurrentOrFirstScene; console.Commands.AddCommand(capitalizedTopic, false,
topic + " " + command,
if (s != null && s.GetCommanders() != null) topic + " " + commander.Commands[command].ShortHelp(),
{ String.Empty, HandleCommanderCommand);
if (s.GetCommanders().ContainsKey(topic))
commander = s.GetCommanders()[topic];
}
if (commander == null)
continue;
foreach (string command in commander.Commands.Keys)
{
m_console.Commands.AddCommand(capitalizedTopic, false,
topic + " " + command,
topic + " " + commander.Commands[command].ShortHelp(),
String.Empty, HandleCommanderCommand);
}
} }
} }
} }

View File

@ -163,8 +163,8 @@ namespace OpenSim.Region.ClientStack.Linden
m_HostCapsObj.RegisterHandler( m_HostCapsObj.RegisterHandler(
"SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest, "SEED", null)); "SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest, "SEED", null));
m_log.DebugFormat( // m_log.DebugFormat(
"[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID); // "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID);
//m_capsHandlers["MapLayer"] = //m_capsHandlers["MapLayer"] =
// new LLSDStreamhandler<OSDMapRequest, OSDMapLayerResponse>("POST", // new LLSDStreamhandler<OSDMapRequest, OSDMapLayerResponse>("POST",
@ -254,11 +254,12 @@ namespace OpenSim.Region.ClientStack.Linden
public string SeedCapRequest(string request, string path, string param, public string SeedCapRequest(string request, string path, string param,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{ {
// m_log.Debug("[CAPS]: Seed Caps Request in region: " + m_regionName); m_log.DebugFormat(
"[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID);
if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint)) if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint))
{ {
m_log.DebugFormat( m_log.WarnFormat(
"[CAPS]: Unauthorized CAPS client {0} from {1}", "[CAPS]: Unauthorized CAPS client {0} from {1}",
m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint); m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint);

View File

@ -94,7 +94,7 @@ namespace OpenSim.Region.ClientStack.Linden
//scene.CommsManager.HttpServer.AddLLSDHandler("/CAPS/EQG/", EventQueueFallBack); //scene.CommsManager.HttpServer.AddLLSDHandler("/CAPS/EQG/", EventQueueFallBack);
scene.EventManager.OnNewClient += OnNewClient; // scene.EventManager.OnNewClient += OnNewClient;
// TODO: Leaving these open, or closing them when we // TODO: Leaving these open, or closing them when we
// become a child is incorrect. It messes up TP in a big // become a child is incorrect. It messes up TP in a big
@ -102,6 +102,7 @@ namespace OpenSim.Region.ClientStack.Linden
// circuit is there. // circuit is there.
scene.EventManager.OnClientClosed += ClientClosed; scene.EventManager.OnClientClosed += ClientClosed;
scene.EventManager.OnMakeChildAgent += MakeChildAgent; scene.EventManager.OnMakeChildAgent += MakeChildAgent;
scene.EventManager.OnRegisterCaps += OnRegisterCaps; scene.EventManager.OnRegisterCaps += OnRegisterCaps;
@ -110,10 +111,10 @@ namespace OpenSim.Region.ClientStack.Linden
false, false,
"debug eq", "debug eq",
"debug eq [0|1|2]", "debug eq [0|1|2]",
"Turn on event queue debugging" "Turn on event queue debugging\n"
+ "<= 0 - turns off all event queue logging" + " <= 0 - turns off all event queue logging\n"
+ ">= 1 - turns on outgoing event logging" + " >= 1 - turns on outgoing event logging\n"
+ ">= 2 - turns on poll notification", + " >= 2 - turns on poll notification",
HandleDebugEq); HandleDebugEq);
} }
else else
@ -226,16 +227,6 @@ namespace OpenSim.Region.ClientStack.Linden
#endregion #endregion
private void OnNewClient(IClientAPI client)
{
//client.OnLogout += ClientClosed;
}
// private void ClientClosed(IClientAPI client)
// {
// ClientClosed(client.AgentId);
// }
private void ClientClosed(UUID agentID, Scene scene) private void ClientClosed(UUID agentID, Scene scene)
{ {
// m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName); // m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName);

View File

@ -107,7 +107,7 @@ namespace OpenSim.Region.ClientStack.Linden
UUID capID = UUID.Random(); UUID capID = UUID.Random();
m_log.DebugFormat("[REGION CONSOLE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); // m_log.DebugFormat("[REGION CONSOLE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
caps.RegisterHandler( caps.RegisterHandler(
"SimConsoleAsync", "SimConsoleAsync",
new ConsoleHandler("/CAPS/" + capID + "/", "SimConsoleAsync", agentID, this, m_scene)); new ConsoleHandler("/CAPS/" + capID + "/", "SimConsoleAsync", agentID, this, m_scene));

View File

@ -45,7 +45,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public Packet Packet; public Packet Packet;
/// <summary> /// <summary>
/// Default constructor /// No arg constructor.
/// </summary>
public IncomingPacket() {}
/// <summary>
/// Constructor
/// </summary> /// </summary>
/// <param name="client">Reference to the client this packet came from</param> /// <param name="client">Reference to the client this packet came from</param>
/// <param name="packet">Packet data</param> /// <param name="packet">Packet data</param>

View File

@ -347,7 +347,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_moneyBalance; private int m_moneyBalance;
private int m_animationSequenceNumber = 1; private int m_animationSequenceNumber = 1;
private bool m_SendLogoutPacketWhenClosing = true; private bool m_SendLogoutPacketWhenClosing = true;
private AgentUpdateArgs lastarg;
/// <summary>
/// We retain a single AgentUpdateArgs so that we can constantly reuse it rather than construct a new one for
/// every single incoming AgentUpdate. Every client sends 10 AgentUpdate UDP messages per second, even if it
/// is doing absolutely nothing.
/// </summary>
/// <remarks>
/// This does mean that agent updates must be processed synchronously, at least for each client, and called methods
/// cannot retain a reference to it outside of that method.
/// </remarks>
private AgentUpdateArgs m_lastAgentUpdateArgs;
protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>(); protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>();
protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers
@ -3922,7 +3932,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value; List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value;
ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); ImprovedTerseObjectUpdatePacket packet
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
packet.RegionData.TimeDilation = timeDilation; packet.RegionData.TimeDilation = timeDilation;
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
@ -3967,7 +3979,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value; List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;
ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); ImprovedTerseObjectUpdatePacket packet
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
PacketType.ImprovedTerseObjectUpdate);
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
packet.RegionData.TimeDilation = timeDilation; packet.RegionData.TimeDilation = timeDilation;
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
@ -4959,7 +4974,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Y, -64.0f, 64.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Y, -64.0f, 64.0f), data, pos); pos += 2;
Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Z, -64.0f, 64.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Z, -64.0f, 64.0f), data, pos); pos += 2;
ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock(); ImprovedTerseObjectUpdatePacket.ObjectDataBlock block
= PacketPool.Instance.GetDataBlock<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>();
block.Data = data; block.Data = data;
if (textureEntry != null && textureEntry.Length > 0) if (textureEntry != null && textureEntry.Length > 0)
@ -5191,7 +5208,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
protected virtual void RegisterLocalPacketHandlers() protected virtual void RegisterLocalPacketHandlers()
{ {
AddLocalPacketHandler(PacketType.LogoutRequest, HandleLogout); AddLocalPacketHandler(PacketType.LogoutRequest, HandleLogout);
// If AgentUpdate is ever handled asynchronously, then we will also need to construct a new AgentUpdateArgs
// for each AgentUpdate packet.
AddLocalPacketHandler(PacketType.AgentUpdate, HandleAgentUpdate, false); AddLocalPacketHandler(PacketType.AgentUpdate, HandleAgentUpdate, false);
AddLocalPacketHandler(PacketType.ViewerEffect, HandleViewerEffect, false); AddLocalPacketHandler(PacketType.ViewerEffect, HandleViewerEffect, false);
AddLocalPacketHandler(PacketType.AgentCachedTexture, HandleAgentTextureCached, false); AddLocalPacketHandler(PacketType.AgentCachedTexture, HandleAgentTextureCached, false);
AddLocalPacketHandler(PacketType.MultipleObjectUpdate, HandleMultipleObjUpdate, false); AddLocalPacketHandler(PacketType.MultipleObjectUpdate, HandleMultipleObjUpdate, false);
@ -5418,80 +5439,83 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region Scene/Avatar #region Scene/Avatar
private bool HandleAgentUpdate(IClientAPI sener, Packet Pack) private bool HandleAgentUpdate(IClientAPI sener, Packet packet)
{ {
if (OnAgentUpdate != null) if (OnAgentUpdate != null)
{ {
bool update = false; AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet;
AgentUpdatePacket agenUpdate = (AgentUpdatePacket)Pack;
#region Packet Session and User Check #region Packet Session and User Check
if (agenUpdate.AgentData.SessionID != SessionId || agenUpdate.AgentData.AgentID != AgentId) if (agentUpdate.AgentData.SessionID != SessionId || agentUpdate.AgentData.AgentID != AgentId)
{
PacketPool.Instance.ReturnPacket(packet);
return false; return false;
}
#endregion #endregion
AgentUpdatePacket.AgentDataBlock x = agenUpdate.AgentData; bool update = false;
AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData;
// We can only check when we have something to check if (m_lastAgentUpdateArgs != null)
// against.
if (lastarg != null)
{ {
// These should be ordered from most-likely to
// least likely to change. I've made an initial
// guess at that.
update = update =
( (
(x.BodyRotation != lastarg.BodyRotation) || (x.BodyRotation != m_lastAgentUpdateArgs.BodyRotation) ||
(x.CameraAtAxis != lastarg.CameraAtAxis) || (x.CameraAtAxis != m_lastAgentUpdateArgs.CameraAtAxis) ||
(x.CameraCenter != lastarg.CameraCenter) || (x.CameraCenter != m_lastAgentUpdateArgs.CameraCenter) ||
(x.CameraLeftAxis != lastarg.CameraLeftAxis) || (x.CameraLeftAxis != m_lastAgentUpdateArgs.CameraLeftAxis) ||
(x.CameraUpAxis != lastarg.CameraUpAxis) || (x.CameraUpAxis != m_lastAgentUpdateArgs.CameraUpAxis) ||
(x.ControlFlags != lastarg.ControlFlags) || (x.ControlFlags != m_lastAgentUpdateArgs.ControlFlags) ||
(x.Far != lastarg.Far) || (x.Far != m_lastAgentUpdateArgs.Far) ||
(x.Flags != lastarg.Flags) || (x.Flags != m_lastAgentUpdateArgs.Flags) ||
(x.State != lastarg.State) || (x.State != m_lastAgentUpdateArgs.State) ||
(x.HeadRotation != lastarg.HeadRotation) || (x.HeadRotation != m_lastAgentUpdateArgs.HeadRotation) ||
(x.SessionID != lastarg.SessionID) || (x.SessionID != m_lastAgentUpdateArgs.SessionID) ||
(x.AgentID != lastarg.AgentID) (x.AgentID != m_lastAgentUpdateArgs.AgentID)
); );
} }
else else
{ {
m_lastAgentUpdateArgs = new AgentUpdateArgs();
update = true; update = true;
} }
// These should be ordered from most-likely to
// least likely to change. I've made an initial
// guess at that.
if (update) if (update)
{ {
// m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name); // m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name);
AgentUpdateArgs arg = new AgentUpdateArgs(); m_lastAgentUpdateArgs.AgentID = x.AgentID;
arg.AgentID = x.AgentID; m_lastAgentUpdateArgs.BodyRotation = x.BodyRotation;
arg.BodyRotation = x.BodyRotation; m_lastAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis;
arg.CameraAtAxis = x.CameraAtAxis; m_lastAgentUpdateArgs.CameraCenter = x.CameraCenter;
arg.CameraCenter = x.CameraCenter; m_lastAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis;
arg.CameraLeftAxis = x.CameraLeftAxis; m_lastAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis;
arg.CameraUpAxis = x.CameraUpAxis; m_lastAgentUpdateArgs.ControlFlags = x.ControlFlags;
arg.ControlFlags = x.ControlFlags; m_lastAgentUpdateArgs.Far = x.Far;
arg.Far = x.Far; m_lastAgentUpdateArgs.Flags = x.Flags;
arg.Flags = x.Flags; m_lastAgentUpdateArgs.HeadRotation = x.HeadRotation;
arg.HeadRotation = x.HeadRotation; m_lastAgentUpdateArgs.SessionID = x.SessionID;
arg.SessionID = x.SessionID; m_lastAgentUpdateArgs.State = x.State;
arg.State = x.State;
UpdateAgent handlerAgentUpdate = OnAgentUpdate; UpdateAgent handlerAgentUpdate = OnAgentUpdate;
UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate;
lastarg = arg; // save this set of arguments for nexttime
if (handlerPreAgentUpdate != null) if (handlerPreAgentUpdate != null)
OnPreAgentUpdate(this, arg); OnPreAgentUpdate(this, m_lastAgentUpdateArgs);
if (handlerAgentUpdate != null) if (handlerAgentUpdate != null)
OnAgentUpdate(this, arg); OnAgentUpdate(this, m_lastAgentUpdateArgs);
handlerAgentUpdate = null; handlerAgentUpdate = null;
handlerPreAgentUpdate = null; handlerPreAgentUpdate = null;
} }
} }
PacketPool.Instance.ReturnPacket(packet);
return true; return true;
} }
@ -9056,7 +9080,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
#endregion #endregion
switch (Utils.BytesToString(messagePacket.MethodData.Method)) string method = Utils.BytesToString(messagePacket.MethodData.Method);
switch (method)
{ {
case "getinfo": case "getinfo":
if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false)) if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false))
@ -9372,7 +9398,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return true; return true;
default: default:
m_log.Error("EstateOwnerMessage: Unknown method requested\n" + messagePacket); m_log.WarnFormat(
"[LLCLIENTVIEW]: EstateOwnerMessage: Unknown method {0} requested for {1} in {2}",
method, Name, Scene.Name);
for (int i = 0; i < messagePacket.ParamList.Length; i++)
{
EstateOwnerMessagePacket.ParamListBlock block = messagePacket.ParamList[i];
string data = (string)Utils.BytesToString(block.Parameter);
m_log.DebugFormat("[LLCLIENTVIEW]: Param {0}={1}", i, data);
}
return true; return true;
} }
@ -11758,7 +11794,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
logPacket = false; logPacket = false;
if (DebugPacketLevel <= 50 if (DebugPacketLevel <= 50
& (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate)) && (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate))
logPacket = false; logPacket = false;
if (DebugPacketLevel <= 25 && packet.Type == PacketType.ObjectPropertiesFamily) if (DebugPacketLevel <= 25 && packet.Type == PacketType.ObjectPropertiesFamily)
@ -11832,8 +11868,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (!ProcessPacketMethod(packet)) if (!ProcessPacketMethod(packet))
m_log.Warn("[CLIENT]: unhandled packet " + packet.Type); m_log.Warn("[CLIENT]: unhandled packet " + packet.Type);
PacketPool.Instance.ReturnPacket(packet);
} }
private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket) private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket)
@ -12286,7 +12320,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f); ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f);
ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); ImprovedTerseObjectUpdatePacket packet
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
PacketType.ImprovedTerseObjectUpdate);
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
packet.RegionData.TimeDilation = timeDilation; packet.RegionData.TimeDilation = timeDilation;
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1]; packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];

View File

@ -37,6 +37,7 @@ using log4net;
using Nini.Config; using Nini.Config;
using OpenMetaverse.Packets; using OpenMetaverse.Packets;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Framework.Monitoring; using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenMetaverse; using OpenMetaverse;
@ -100,9 +101,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>The measured resolution of Environment.TickCount</summary> /// <summary>The measured resolution of Environment.TickCount</summary>
public readonly float TickCountResolution; public readonly float TickCountResolution;
/// <summary>Number of prim updates to put on the queue each time the /// <summary>Number of prim updates to put on the queue each time the
/// OnQueueEmpty event is triggered for updates</summary> /// OnQueueEmpty event is triggered for updates</summary>
public readonly int PrimUpdatesPerCallback; public readonly int PrimUpdatesPerCallback;
/// <summary>Number of texture packets to put on the queue each time the /// <summary>Number of texture packets to put on the queue each time the
/// OnQueueEmpty event is triggered for textures</summary> /// OnQueueEmpty event is triggered for textures</summary>
public readonly int TextureSendLimit; public readonly int TextureSendLimit;
@ -111,6 +114,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
//PacketEventDictionary packetEvents = new PacketEventDictionary(); //PacketEventDictionary packetEvents = new PacketEventDictionary();
/// <summary>Incoming packets that are awaiting handling</summary> /// <summary>Incoming packets that are awaiting handling</summary>
private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>(); private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
/// <summary></summary> /// <summary></summary>
//private UDPClientCollection m_clients = new UDPClientCollection(); //private UDPClientCollection m_clients = new UDPClientCollection();
/// <summary>Bandwidth throttle for this UDP server</summary> /// <summary>Bandwidth throttle for this UDP server</summary>
@ -121,28 +125,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>Manages authentication for agent circuits</summary> /// <summary>Manages authentication for agent circuits</summary>
private AgentCircuitManager m_circuitManager; private AgentCircuitManager m_circuitManager;
/// <summary>Reference to the scene this UDP server is attached to</summary> /// <summary>Reference to the scene this UDP server is attached to</summary>
protected Scene m_scene; protected Scene m_scene;
/// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary> /// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary>
private Location m_location; private Location m_location;
/// <summary>The size of the receive buffer for the UDP socket. This value /// <summary>The size of the receive buffer for the UDP socket. This value
/// is passed up to the operating system and used in the system networking /// is passed up to the operating system and used in the system networking
/// stack. Use zero to leave this value as the default</summary> /// stack. Use zero to leave this value as the default</summary>
private int m_recvBufferSize; private int m_recvBufferSize;
/// <summary>Flag to process packets asynchronously or synchronously</summary> /// <summary>Flag to process packets asynchronously or synchronously</summary>
private bool m_asyncPacketHandling; private bool m_asyncPacketHandling;
/// <summary>Tracks whether or not a packet was sent each round so we know /// <summary>Tracks whether or not a packet was sent each round so we know
/// whether or not to sleep</summary> /// whether or not to sleep</summary>
private bool m_packetSent; private bool m_packetSent;
/// <summary>Environment.TickCount of the last time that packet stats were reported to the scene</summary> /// <summary>Environment.TickCount of the last time that packet stats were reported to the scene</summary>
private int m_elapsedMSSinceLastStatReport = 0; private int m_elapsedMSSinceLastStatReport = 0;
/// <summary>Environment.TickCount of the last time the outgoing packet handler executed</summary> /// <summary>Environment.TickCount of the last time the outgoing packet handler executed</summary>
private int m_tickLastOutgoingPacketHandler; private int m_tickLastOutgoingPacketHandler;
/// <summary>Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped</summary> /// <summary>Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped</summary>
private int m_elapsedMSOutgoingPacketHandler; private int m_elapsedMSOutgoingPacketHandler;
/// <summary>Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed</summary> /// <summary>Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed</summary>
private int m_elapsed100MSOutgoingPacketHandler; private int m_elapsed100MSOutgoingPacketHandler;
/// <summary>Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed</summary> /// <summary>Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed</summary>
private int m_elapsed500MSOutgoingPacketHandler; private int m_elapsed500MSOutgoingPacketHandler;
@ -155,6 +168,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>Flag to signal when clients should send pings</summary> /// <summary>Flag to signal when clients should send pings</summary>
protected bool m_sendPing; protected bool m_sendPing;
private Pool<IncomingPacket> m_incomingPacketPool;
private int m_defaultRTO = 0; private int m_defaultRTO = 0;
private int m_maxRTO = 0; private int m_maxRTO = 0;
private int m_ackTimeout = 0; private int m_ackTimeout = 0;
@ -175,7 +190,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </summary> /// </summary>
private IClientAPI m_currentIncomingClient; private IClientAPI m_currentIncomingClient;
public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) public LLUDPServer(
IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port,
IConfigSource configSource, AgentCircuitManager circuitManager)
: base(listenIP, (int)port) : base(listenIP, (int)port)
{ {
#region Environment.TickCount Measurement #region Environment.TickCount Measurement
@ -229,6 +246,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true); PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true);
PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true); PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true);
UsePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", false);
} }
#region BinaryStats #region BinaryStats
@ -258,20 +276,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_throttle = new TokenBucket(null, sceneThrottleBps); m_throttle = new TokenBucket(null, sceneThrottleBps);
ThrottleRates = new ThrottleRates(configSource); ThrottleRates = new ThrottleRates(configSource);
if (UsePools)
m_incomingPacketPool = new Pool<IncomingPacket>(() => new IncomingPacket(), 500);
} }
public void Start() public void Start()
{ {
if (m_scene == null) StartInbound();
throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); StartOutbound();
m_elapsedMSSinceLastStatReport = Environment.TickCount;
}
private void StartInbound()
{
m_log.InfoFormat( m_log.InfoFormat(
"[LLUDPSERVER]: Starting the LLUDP server in {0} mode", "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
m_asyncPacketHandling ? "asynchronous" : "synchronous"); m_asyncPacketHandling ? "asynchronous" : "synchronous", UsePools);
base.Start(m_recvBufferSize, m_asyncPacketHandling); base.StartInbound(m_recvBufferSize, m_asyncPacketHandling);
// Start the packet processing threads // This thread will process the packets received that are placed on the packetInbox
Watchdog.StartThread( Watchdog.StartThread(
IncomingPacketHandler, IncomingPacketHandler,
string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName), string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName),
@ -280,6 +306,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
true, true,
GetWatchdogIncomingAlarmData, GetWatchdogIncomingAlarmData,
Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS); Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
}
private new void StartOutbound()
{
m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server");
base.StartOutbound();
Watchdog.StartThread( Watchdog.StartThread(
OutgoingPacketHandler, OutgoingPacketHandler,
@ -289,8 +322,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
true, true,
GetWatchdogOutgoingAlarmData, GetWatchdogOutgoingAlarmData,
Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS); Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
}
m_elapsedMSSinceLastStatReport = Environment.TickCount; public void Stop()
{
m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
base.StopOutbound();
base.StopInbound();
} }
/// <summary> /// <summary>
@ -315,12 +353,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none"); m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none");
} }
public new void Stop()
{
m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
base.Stop();
}
public void AddScene(IScene scene) public void AddScene(IScene scene)
{ {
if (m_scene != null) if (m_scene != null)
@ -337,6 +369,81 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_scene = (Scene)scene; m_scene = (Scene)scene;
m_location = new Location(m_scene.RegionInfo.RegionHandle); m_location = new Location(m_scene.RegionInfo.RegionHandle);
MainConsole.Instance.Commands.AddCommand(
"Debug",
false,
"debug lludp start",
"debug lludp start <in|out|all>",
"Control LLUDP packet processing.",
"No effect if packet processing has already started.\n"
+ "in - start inbound processing.\n"
+ "out - start outbound processing.\n"
+ "all - start in and outbound processing.\n",
HandleStartCommand);
MainConsole.Instance.Commands.AddCommand(
"Debug",
false,
"debug lludp stop",
"debug lludp stop <in|out|all>",
"Stop LLUDP packet processing.",
"No effect if packet processing has already stopped.\n"
+ "in - stop inbound processing.\n"
+ "out - stop outbound processing.\n"
+ "all - stop in and outbound processing.\n",
HandleStopCommand);
MainConsole.Instance.Commands.AddCommand(
"Debug",
false,
"debug lludp status",
"debug lludp status",
"Return status of LLUDP packet processing.",
HandleStatusCommand);
}
private void HandleStartCommand(string module, string[] args)
{
if (args.Length != 4)
{
MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>");
return;
}
string subCommand = args[3];
if (subCommand == "in" || subCommand == "all")
StartInbound();
if (subCommand == "out" || subCommand == "all")
StartOutbound();
}
private void HandleStopCommand(string module, string[] args)
{
if (args.Length != 4)
{
MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>");
return;
}
string subCommand = args[3];
if (subCommand == "in" || subCommand == "all")
StopInbound();
if (subCommand == "out" || subCommand == "all")
StopOutbound();
}
private void HandleStatusCommand(string module, string[] args)
{
MainConsole.Instance.OutputFormat(
"IN LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningInbound ? "enabled" : "disabled");
MainConsole.Instance.OutputFormat(
"OUT LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningOutbound ? "enabled" : "disabled");
} }
public bool HandlesRegion(Location x) public bool HandlesRegion(Location x)
@ -420,6 +527,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
byte[] data = packet.ToBytes(); byte[] data = packet.ToBytes();
SendPacketData(udpClient, data, packet.Type, category, method); SendPacketData(udpClient, data, packet.Type, category, method);
} }
PacketPool.Instance.ReturnPacket(packet);
} }
/// <summary> /// <summary>
@ -704,7 +813,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
LLUDPClient udpClient = null; LLUDPClient udpClient = null;
Packet packet = null; Packet packet = null;
int packetEnd = buffer.DataLength - 1; int packetEnd = buffer.DataLength - 1;
IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; IPEndPoint endPoint = (IPEndPoint)buffer.RemoteEndPoint;
#region Decoding #region Decoding
@ -714,7 +823,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}", // "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}",
// buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
return; // Drop undersizd packet return; // Drop undersized packet
} }
int headerLen = 7; int headerLen = 7;
@ -737,7 +846,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
try try
{ {
packet = Packet.BuildPacket(buffer.Data, ref packetEnd, // packet = Packet.BuildPacket(buffer.Data, ref packetEnd,
// // Only allocate a buffer for zerodecoding if the packet is zerocoded
// ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
// If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we
// assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all
// bytes are copied out).
packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd,
// Only allocate a buffer for zerodecoding if the packet is zerocoded // Only allocate a buffer for zerodecoding if the packet is zerocoded
((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
} }
@ -752,11 +867,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return; // Drop short packet return; // Drop short packet
} }
catch(Exception e) catch (Exception e)
{ {
if (m_malformedCount < 100) if (m_malformedCount < 100)
m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString()); m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString());
m_malformedCount++; m_malformedCount++;
if ((m_malformedCount % 100000) == 0) if ((m_malformedCount % 100000) == 0)
m_log.DebugFormat("[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack.", m_malformedCount); m_log.DebugFormat("[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack.", m_malformedCount);
} }
@ -777,7 +894,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// UseCircuitCode handling // UseCircuitCode handling
if (packet.Type == PacketType.UseCircuitCode) if (packet.Type == PacketType.UseCircuitCode)
{ {
object[] array = new object[] { buffer, packet }; // We need to copy the endpoint so that it doesn't get changed when another thread reuses the
// buffer.
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
Util.FireAndForget(HandleUseCircuitCode, array); Util.FireAndForget(HandleUseCircuitCode, array);
@ -786,7 +905,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Determine which agent this packet came from // Determine which agent this packet came from
IClientAPI client; IClientAPI client;
if (!m_scene.TryGetClient(address, out client) || !(client is LLClientView)) if (!m_scene.TryGetClient(endPoint, out client) || !(client is LLClientView))
{ {
//m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName);
return; return;
@ -810,6 +929,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Handle appended ACKs // Handle appended ACKs
if (packet.Header.AppendedAcks && packet.Header.AckList != null) if (packet.Header.AppendedAcks && packet.Header.AckList != null)
{ {
// m_log.DebugFormat(
// "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}",
// packet.Header.AckList.Length, client.Name, m_scene.Name);
for (int i = 0; i < packet.Header.AckList.Length; i++) for (int i = 0; i < packet.Header.AckList.Length; i++)
udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent); udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent);
} }
@ -819,6 +942,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
PacketAckPacket ackPacket = (PacketAckPacket)packet; PacketAckPacket ackPacket = (PacketAckPacket)packet;
// m_log.DebugFormat(
// "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}",
// ackPacket.Packets.Length, client.Name, m_scene.Name);
for (int i = 0; i < ackPacket.Packets.Length; i++) for (int i = 0; i < ackPacket.Packets.Length; i++)
udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent); udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent);
@ -832,6 +959,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (packet.Header.Reliable) if (packet.Header.Reliable)
{ {
// m_log.DebugFormat(
// "[LLUDPSERVER]: Adding ack request for {0} {1} from {2} in {3}",
// packet.Type, packet.Header.Sequence, client.Name, m_scene.Name);
udpClient.PendingAcks.Enqueue(packet.Header.Sequence); udpClient.PendingAcks.Enqueue(packet.Header.Sequence);
// This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out,
@ -878,6 +1009,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (packet.Type == PacketType.StartPingCheck) if (packet.Type == PacketType.StartPingCheck)
{ {
// m_log.DebugFormat("[LLUDPSERVER]: Handling ping from {0} in {1}", client.Name, m_scene.Name);
// We don't need to do anything else with ping checks // We don't need to do anything else with ping checks
StartPingCheckPacket startPing = (StartPingCheckPacket)packet; StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
CompletePing(udpClient, startPing.PingID.PingID); CompletePing(udpClient, startPing.PingID.PingID);
@ -897,8 +1030,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion Ping Check Handling #endregion Ping Check Handling
IncomingPacket incomingPacket;
// Inbox insertion // Inbox insertion
packetInbox.Enqueue(new IncomingPacket((LLClientView)client, packet)); if (UsePools)
{
incomingPacket = m_incomingPacketPool.GetObject();
incomingPacket.Client = (LLClientView)client;
incomingPacket.Packet = packet;
}
else
{
incomingPacket = new IncomingPacket((LLClientView)client, packet);
}
packetInbox.Enqueue(incomingPacket);
} }
#region BinaryStats #region BinaryStats
@ -984,21 +1130,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void HandleUseCircuitCode(object o) private void HandleUseCircuitCode(object o)
{ {
IPEndPoint remoteEndPoint = null; IPEndPoint endPoint = null;
IClientAPI client = null; IClientAPI client = null;
try try
{ {
// DateTime startTime = DateTime.Now; // DateTime startTime = DateTime.Now;
object[] array = (object[])o; object[] array = (object[])o;
UDPPacketBuffer buffer = (UDPPacketBuffer)array[0]; endPoint = (IPEndPoint)array[0];
UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1]; UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1];
m_log.DebugFormat( m_log.DebugFormat(
"[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}", "[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}",
uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, buffer.RemoteEndPoint); uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, endPoint);
remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint;
AuthenticateResponse sessionInfo; AuthenticateResponse sessionInfo;
if (IsClientAuthorized(uccp, out sessionInfo)) if (IsClientAuthorized(uccp, out sessionInfo))
@ -1009,13 +1153,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
uccp.CircuitCode.Code, uccp.CircuitCode.Code,
uccp.CircuitCode.ID, uccp.CircuitCode.ID,
uccp.CircuitCode.SessionID, uccp.CircuitCode.SessionID,
remoteEndPoint, endPoint,
sessionInfo); sessionInfo);
// Send ack straight away to let the viewer know that the connection is active. // Send ack straight away to let the viewer know that the connection is active.
// The client will be null if it already exists (e.g. if on a region crossing the client sends a use // The client will be null if it already exists (e.g. if on a region crossing the client sends a use
// circuit code to the existing child agent. This is not particularly obvious. // circuit code to the existing child agent. This is not particularly obvious.
SendAckImmediate(remoteEndPoint, uccp.Header.Sequence); SendAckImmediate(endPoint, uccp.Header.Sequence);
// We only want to send initial data to new clients, not ones which are being converted from child to root. // We only want to send initial data to new clients, not ones which are being converted from child to root.
if (client != null) if (client != null)
@ -1026,7 +1170,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Don't create clients for unauthorized requesters. // Don't create clients for unauthorized requesters.
m_log.WarnFormat( m_log.WarnFormat(
"[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}", "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}",
uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint); uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint);
} }
// m_log.DebugFormat( // m_log.DebugFormat(
@ -1038,7 +1182,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
m_log.ErrorFormat( m_log.ErrorFormat(
"[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}", "[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}",
remoteEndPoint != null ? remoteEndPoint.ToString() : "n/a", endPoint != null ? endPoint.ToString() : "n/a",
client != null ? client.Name : "unknown", client != null ? client.Name : "unknown",
client != null ? client.AgentId.ToString() : "unknown", client != null ? client.AgentId.ToString() : "unknown",
e.Message, e.Message,
@ -1103,20 +1247,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
IClientAPI client = null; IClientAPI client = null;
// In priciple there shouldn't be more than one thread here, ever. // We currently synchronize this code across the whole scene to avoid issues such as
// But in case that happens, we need to synchronize this piece of code // http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done
// because it's too important // consistently, this lock could probably be removed.
lock (this) lock (this)
{ {
if (!m_scene.TryGetClient(agentID, out client)) if (!m_scene.TryGetClient(agentID, out client))
{ {
LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
client.OnLogout += LogoutHandler; client.OnLogout += LogoutHandler;
((LLClientView)client).DisableFacelights = m_disableFacelights; ((LLClientView)client).DisableFacelights = m_disableFacelights;
client.Start(); client.Start();
} }
} }
@ -1155,7 +1299,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// on to en-US to avoid number parsing issues // on to en-US to avoid number parsing issues
Culture.SetCurrentCulture(); Culture.SetCurrentCulture();
while (base.IsRunning) while (IsRunningInbound)
{ {
try try
{ {
@ -1170,7 +1314,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
if (packetInbox.Dequeue(100, ref incomingPacket)) if (packetInbox.Dequeue(100, ref incomingPacket))
{
ProcessInPacket(incomingPacket);//, incomingPacket); Util.FireAndForget(ProcessInPacket, incomingPacket); ProcessInPacket(incomingPacket);//, incomingPacket); Util.FireAndForget(ProcessInPacket, incomingPacket);
if (UsePools)
m_incomingPacketPool.ReturnObject(incomingPacket);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -1197,7 +1346,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Action generic every round // Action generic every round
Action<IClientAPI> clientPacketHandler = ClientOutgoingPacketHandler; Action<IClientAPI> clientPacketHandler = ClientOutgoingPacketHandler;
while (base.IsRunning) while (base.IsRunningOutbound)
{ {
try try
{ {

View File

@ -30,6 +30,7 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
using log4net; using log4net;
using OpenSim.Framework;
namespace OpenMetaverse namespace OpenMetaverse
{ {
@ -58,17 +59,29 @@ namespace OpenMetaverse
/// <summary>Flag to process packets asynchronously or synchronously</summary> /// <summary>Flag to process packets asynchronously or synchronously</summary>
private bool m_asyncPacketHandling; private bool m_asyncPacketHandling;
/// <summary>The all important shutdown flag</summary> /// <summary>
private volatile bool m_shutdownFlag = true; /// Pool to use for handling data. May be null if UsePools = false;
/// </summary>
protected OpenSim.Framework.Pool<UDPPacketBuffer> m_pool;
/// <summary>Returns true if the server is currently listening, otherwise false</summary> /// <summary>
public bool IsRunning { get { return !m_shutdownFlag; } } /// Are we to use object pool(s) to reduce memory churn when receiving data?
/// </summary>
public bool UsePools { get; protected set; }
/// <summary>Returns true if the server is currently listening for inbound packets, otherwise false</summary>
public bool IsRunningInbound { get; private set; }
/// <summary>Returns true if the server is currently sending outbound packets, otherwise false</summary>
/// <remarks>If IsRunningOut = false, then any request to send a packet is simply dropped.</remarks>
public bool IsRunningOutbound { get; private set; }
/// <summary> /// <summary>
/// Default constructor /// Default constructor
/// </summary> /// </summary>
/// <param name="bindAddress">Local IP address to bind the server to</param> /// <param name="bindAddress">Local IP address to bind the server to</param>
/// <param name="port">Port to listening for incoming UDP packets on</param> /// <param name="port">Port to listening for incoming UDP packets on</param>
/// /// <param name="usePool">Are we to use an object pool to get objects for handing inbound data?</param>
public OpenSimUDPBase(IPAddress bindAddress, int port) public OpenSimUDPBase(IPAddress bindAddress, int port)
{ {
m_localBindAddress = bindAddress; m_localBindAddress = bindAddress;
@ -76,7 +89,7 @@ namespace OpenMetaverse
} }
/// <summary> /// <summary>
/// Start the UDP server /// Start inbound UDP packet handling.
/// </summary> /// </summary>
/// <param name="recvBufferSize">The size of the receive buffer for /// <param name="recvBufferSize">The size of the receive buffer for
/// the UDP socket. This value is passed up to the operating system /// the UDP socket. This value is passed up to the operating system
@ -91,11 +104,16 @@ namespace OpenMetaverse
/// manner (not throwing an exception when the remote side resets the /// manner (not throwing an exception when the remote side resets the
/// connection). This call is ignored on Mono where the flag is not /// connection). This call is ignored on Mono where the flag is not
/// necessary</remarks> /// necessary</remarks>
public void Start(int recvBufferSize, bool asyncPacketHandling) public void StartInbound(int recvBufferSize, bool asyncPacketHandling)
{ {
if (UsePools)
m_pool = new Pool<UDPPacketBuffer>(() => new UDPPacketBuffer(), 500);
else
m_pool = null;
m_asyncPacketHandling = asyncPacketHandling; m_asyncPacketHandling = asyncPacketHandling;
if (m_shutdownFlag) if (!IsRunningInbound)
{ {
const int SIO_UDP_CONNRESET = -1744830452; const int SIO_UDP_CONNRESET = -1744830452;
@ -127,8 +145,7 @@ namespace OpenMetaverse
m_udpSocket.Bind(ipep); m_udpSocket.Bind(ipep);
// we're not shutting down, we're starting up IsRunningInbound = true;
m_shutdownFlag = false;
// kick off an async receive. The Start() method will return, the // kick off an async receive. The Start() method will return, the
// actual receives will occur asynchronously and will be caught in // actual receives will occur asynchronously and will be caught in
@ -138,28 +155,41 @@ namespace OpenMetaverse
} }
/// <summary> /// <summary>
/// Stops the UDP server /// Start outbound UDP packet handling.
/// </summary> /// </summary>
public void Stop() public void StartOutbound()
{ {
if (!m_shutdownFlag) IsRunningOutbound = true;
}
public void StopInbound()
{
if (IsRunningInbound)
{ {
// wait indefinitely for a writer lock. Once this is called, the .NET runtime // wait indefinitely for a writer lock. Once this is called, the .NET runtime
// will deny any more reader locks, in effect blocking all other send/receive // will deny any more reader locks, in effect blocking all other send/receive
// threads. Once we have the lock, we set shutdownFlag to inform the other // threads. Once we have the lock, we set IsRunningInbound = false to inform the other
// threads that the socket is closed. // threads that the socket is closed.
m_shutdownFlag = true; IsRunningInbound = false;
m_udpSocket.Close(); m_udpSocket.Close();
} }
} }
public void StopOutbound()
{
IsRunningOutbound = false;
}
private void AsyncBeginReceive() private void AsyncBeginReceive()
{ {
// allocate a packet buffer UDPPacketBuffer buf;
//WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
UDPPacketBuffer buf = new UDPPacketBuffer();
if (!m_shutdownFlag) if (UsePools)
buf = m_pool.GetObject();
else
buf = new UDPPacketBuffer();
if (IsRunningInbound)
{ {
try try
{ {
@ -212,7 +242,7 @@ namespace OpenMetaverse
{ {
// Asynchronous receive operations will complete here through the call // Asynchronous receive operations will complete here through the call
// to AsyncBeginReceive // to AsyncBeginReceive
if (!m_shutdownFlag) if (IsRunningInbound)
{ {
// Asynchronous mode will start another receive before the // Asynchronous mode will start another receive before the
// callback for this packet is even fired. Very parallel :-) // callback for this packet is even fired. Very parallel :-)
@ -221,8 +251,6 @@ namespace OpenMetaverse
// get the buffer that was created in AsyncBeginReceive // get the buffer that was created in AsyncBeginReceive
// this is the received data // this is the received data
//WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState;
//UDPPacketBuffer buffer = wrappedBuffer.Instance;
UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
try try
@ -239,7 +267,8 @@ namespace OpenMetaverse
catch (ObjectDisposedException) { } catch (ObjectDisposedException) { }
finally finally
{ {
//wrappedBuffer.Dispose(); if (UsePools)
m_pool.ReturnObject(buffer);
// Synchronous mode waits until the packet callback completes // Synchronous mode waits until the packet callback completes
// before starting the receive to fetch another packet // before starting the receive to fetch another packet
@ -252,7 +281,7 @@ namespace OpenMetaverse
public void AsyncBeginSend(UDPPacketBuffer buf) public void AsyncBeginSend(UDPPacketBuffer buf)
{ {
if (!m_shutdownFlag) if (IsRunningOutbound)
{ {
try try
{ {

View File

@ -31,6 +31,7 @@ using System.Reflection;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.Packets; using OpenMetaverse.Packets;
using log4net; using log4net;
using OpenSim.Framework.Monitoring;
namespace OpenSim.Region.ClientStack.LindenUDP namespace OpenSim.Region.ClientStack.LindenUDP
{ {
@ -43,17 +44,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private bool packetPoolEnabled = true; private bool packetPoolEnabled = true;
private bool dataBlockPoolEnabled = true; private bool dataBlockPoolEnabled = true;
private PercentageStat m_packetsReusedStat = new PercentageStat(
"PacketsReused",
"Packets reused",
"clientstack",
"packetpool",
StatVerbosity.Debug,
"Number of packets reused out of all requests to the packet pool");
private PercentageStat m_blocksReusedStat = new PercentageStat(
"BlocksReused",
"Blocks reused",
"clientstack",
"packetpool",
StatVerbosity.Debug,
"Number of data blocks reused out of all requests to the packet pool");
/// <summary> /// <summary>
/// Pool of packets available for reuse. /// Pool of packets available for reuse.
/// </summary> /// </summary>
private readonly Dictionary<PacketType, Stack<Packet>> pool = new Dictionary<PacketType, Stack<Packet>>(); private readonly Dictionary<PacketType, Stack<Packet>> pool = new Dictionary<PacketType, Stack<Packet>>();
private static Dictionary<Type, Stack<Object>> DataBlocks = private static Dictionary<Type, Stack<Object>> DataBlocks = new Dictionary<Type, Stack<Object>>();
new Dictionary<Type, Stack<Object>>();
static PacketPool()
{
}
public static PacketPool Instance public static PacketPool Instance
{ {
@ -72,8 +84,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
get { return dataBlockPoolEnabled; } get { return dataBlockPoolEnabled; }
} }
private PacketPool()
{
StatsManager.RegisterStat(m_packetsReusedStat);
StatsManager.RegisterStat(m_blocksReusedStat);
}
/// <summary>
/// Gets a packet of the given type.
/// </summary>
/// <param name='type'></param>
/// <returns>Guaranteed to always return a packet, whether from the pool or newly constructed.</returns>
public Packet GetPacket(PacketType type) public Packet GetPacket(PacketType type)
{ {
m_packetsReusedStat.Consequent++;
Packet packet; Packet packet;
if (!packetPoolEnabled) if (!packetPoolEnabled)
@ -83,13 +108,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
if (!pool.ContainsKey(type) || pool[type] == null || (pool[type]).Count == 0) if (!pool.ContainsKey(type) || pool[type] == null || (pool[type]).Count == 0)
{ {
// m_log.DebugFormat("[PACKETPOOL]: Building {0} packet", type);
// Creating a new packet if we cannot reuse an old package // Creating a new packet if we cannot reuse an old package
packet = Packet.BuildPacket(type); packet = Packet.BuildPacket(type);
} }
else else
{ {
// m_log.DebugFormat("[PACKETPOOL]: Pulling {0} packet", type);
// Recycle old packages // Recycle old packages
packet = (pool[type]).Pop(); m_packetsReusedStat.Antecedent++;
packet = pool[type].Pop();
} }
} }
@ -138,7 +169,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
PacketType type = GetType(bytes); PacketType type = GetType(bytes);
Array.Clear(zeroBuffer, 0, zeroBuffer.Length); // Array.Clear(zeroBuffer, 0, zeroBuffer.Length);
int i = 0; int i = 0;
Packet packet = GetPacket(type); Packet packet = GetPacket(type);
@ -185,6 +216,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
switch (packet.Type) switch (packet.Type)
{ {
// List pooling packets here // List pooling packets here
case PacketType.AgentUpdate:
case PacketType.PacketAck: case PacketType.PacketAck:
case PacketType.ObjectUpdate: case PacketType.ObjectUpdate:
case PacketType.ImprovedTerseObjectUpdate: case PacketType.ImprovedTerseObjectUpdate:
@ -199,7 +231,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if ((pool[type]).Count < 50) if ((pool[type]).Count < 50)
{ {
(pool[type]).Push(packet); // m_log.DebugFormat("[PACKETPOOL]: Pushing {0} packet", type);
pool[type].Push(packet);
} }
} }
break; break;
@ -211,16 +245,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
public static T GetDataBlock<T>() where T: new() public T GetDataBlock<T>() where T: new()
{ {
lock (DataBlocks) lock (DataBlocks)
{ {
m_blocksReusedStat.Consequent++;
Stack<Object> s; Stack<Object> s;
if (DataBlocks.TryGetValue(typeof(T), out s)) if (DataBlocks.TryGetValue(typeof(T), out s))
{ {
if (s.Count > 0) if (s.Count > 0)
{
m_blocksReusedStat.Antecedent++;
return (T)s.Pop(); return (T)s.Pop();
}
} }
else else
{ {
@ -231,7 +270,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
public static void ReturnDataBlock<T>(T block) where T: new() public void ReturnDataBlock<T>(T block) where T: new()
{ {
if (block == null) if (block == null)
return; return;

View File

@ -43,7 +43,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
/// This will contain basic tests for the LindenUDP client stack /// This will contain basic tests for the LindenUDP client stack
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class BasicCircuitTests public class BasicCircuitTests : OpenSimTestCase
{ {
private Scene m_scene; private Scene m_scene;
private TestLLUDPServer m_udpServer; private TestLLUDPServer m_udpServer;
@ -65,8 +65,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
} }
[SetUp] [SetUp]
public void SetUp() public override void SetUp()
{ {
base.SetUp();
m_scene = new SceneHelpers().SetupScene(); m_scene = new SceneHelpers().SetupScene();
} }
@ -143,7 +144,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
public void TestAddClient() public void TestAddClient()
{ {
TestHelpers.InMethod(); TestHelpers.InMethod();
// XmlConfigurator.Configure(); // TestHelpers.EnableLogging();
AddUdpServer(); AddUdpServer();

View File

@ -28,6 +28,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using log4net; using log4net;
@ -495,42 +496,36 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
protected virtual void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online) protected virtual void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online)
{ {
foreach (FriendInfo friend in friendList) List<string> friendStringIds = friendList.ConvertAll<string>(friend => friend.Friend);
List<string> remoteFriendStringIds = new List<string>();
foreach (string friendStringId in friendStringIds)
{ {
UUID friendID; UUID friendUuid;
if (UUID.TryParse(friend.Friend, out friendID)) if (UUID.TryParse(friendStringId, out friendUuid))
{ {
// Try local if (LocalStatusNotification(userID, friendUuid, online))
if (LocalStatusNotification(userID, friendID, online))
continue; continue;
// The friend is not here [as root]. Let's forward. remoteFriendStringIds.Add(friendStringId);
PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
if (friendSessions != null && friendSessions.Length > 0)
{
PresenceInfo friendSession = null;
foreach (PresenceInfo pinfo in friendSessions)
{
if (pinfo.RegionID != UUID.Zero) // let's guard against sessions-gone-bad
{
friendSession = pinfo;
break;
}
}
if (friendSession != null)
{
GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
//m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName);
m_FriendsSimConnector.StatusNotify(region, userID, friendID, online);
}
}
// Friend is not online. Ignore.
} }
else else
{ {
m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friend.Friend); m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friendStringId);
}
}
// We do this regrouping so that we can efficiently send a single request rather than one for each
// friend in what may be a very large friends list.
PresenceInfo[] friendSessions = PresenceService.GetAgents(remoteFriendStringIds.ToArray());
foreach (PresenceInfo friendSession in friendSessions)
{
// let's guard against sessions-gone-bad
if (friendSession.RegionID != UUID.Zero)
{
GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
//m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName);
m_FriendsSimConnector.StatusNotify(region, userID, friendSession.UserID, online);
} }
} }
} }

View File

@ -486,6 +486,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
if (sp.ParentID != (uint)0) if (sp.ParentID != (uint)0)
sp.StandUp(); sp.StandUp();
// At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to
// the viewer. However, it might mean that the viewer does not see the black teleport screen (untested).
sp.ControllingClient.SendTeleportStart(teleportFlags); sp.ControllingClient.SendTeleportStart(teleportFlags);
// the avatar.Close below will clear the child region list. We need this below for (possibly) // the avatar.Close below will clear the child region list. We need this below for (possibly)
@ -561,8 +563,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// So let's wait // So let's wait
Thread.Sleep(200); Thread.Sleep(200);
// At least on LL 3.3.4 for teleports between different regions on the same simulator this appears
// unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly
// only on TeleportFinish). This is untested for region teleport between different simulators
// though this probably also works.
m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath); m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath);
} }
else else
{ {

View File

@ -42,7 +42,7 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
{ {
public class DynamicTextureModule : IRegionModule, IDynamicTextureManager public class DynamicTextureModule : IRegionModule, IDynamicTextureManager
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private const int ALL_SIDES = -1; private const int ALL_SIDES = -1;

View File

@ -290,7 +290,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
foreach (DearchiveContext sceneContext in sceneContexts.Values) foreach (DearchiveContext sceneContext in sceneContexts.Values)
{ {
m_log.InfoFormat("[ARCHIVER:] Loading region {0}", sceneContext.Scene.RegionInfo.RegionName); m_log.InfoFormat("[ARCHIVER]: Loading region {0}", sceneContext.Scene.RegionInfo.RegionName);
if (!m_merge) if (!m_merge)
{ {
@ -324,7 +324,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
Util.FireAndForget(delegate(object o) Util.FireAndForget(delegate(object o)
{ {
Thread.Sleep(15000); Thread.Sleep(15000);
m_log.Info("Starting scripts in scene objects"); m_log.Info("[ARCHIVER]: Starting scripts in scene objects");
foreach (DearchiveContext sceneContext in sceneContexts.Values) foreach (DearchiveContext sceneContext in sceneContexts.Values)
{ {

View File

@ -207,6 +207,7 @@ namespace OpenSim.Region.CoreModules.World.Land
client.OnParcelInfoRequest += ClientOnParcelInfoRequest; client.OnParcelInfoRequest += ClientOnParcelInfoRequest;
client.OnParcelDeedToGroup += ClientOnParcelDeedToGroup; client.OnParcelDeedToGroup += ClientOnParcelDeedToGroup;
client.OnPreAgentUpdate += ClientOnPreAgentUpdate; client.OnPreAgentUpdate += ClientOnPreAgentUpdate;
client.OnParcelDwellRequest += ClientOnParcelDwellRequest;
EntityBase presenceEntity; EntityBase presenceEntity;
if (m_scene.Entities.TryGetValue(client.AgentId, out presenceEntity) && presenceEntity is ScenePresence) if (m_scene.Entities.TryGetValue(client.AgentId, out presenceEntity) && presenceEntity is ScenePresence)
@ -798,6 +799,17 @@ namespace OpenSim.Region.CoreModules.World.Land
} }
} }
private void ClientOnParcelDwellRequest(int localID, IClientAPI client)
{
ILandObject parcel = null;
lock (m_landList)
{
if (!m_landList.TryGetValue(localID, out parcel))
return;
}
client.SendParcelDwellReply(localID, parcel.LandData.GlobalID, parcel.LandData.Dwell);
}
#endregion #endregion
#region Parcel Modification #region Parcel Modification

View File

@ -27,6 +27,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -83,53 +84,87 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
m_console.Commands.AddCommand( m_console.Commands.AddCommand(
"Objects", false, "delete object owner", "Objects", false, "delete object owner",
"delete object owner <UUID>", "delete object owner <UUID>",
"Delete a scene object by owner", HandleDeleteObject); "Delete scene objects by owner",
"Command will ask for confirmation before proceeding.",
HandleDeleteObject);
m_console.Commands.AddCommand( m_console.Commands.AddCommand(
"Objects", false, "delete object creator", "Objects", false, "delete object creator",
"delete object creator <UUID>", "delete object creator <UUID>",
"Delete a scene object by creator", HandleDeleteObject); "Delete scene objects by creator",
"Command will ask for confirmation before proceeding.",
HandleDeleteObject);
m_console.Commands.AddCommand( m_console.Commands.AddCommand(
"Objects", false, "delete object uuid", "Objects", false, "delete object uuid",
"delete object uuid <UUID>", "delete object uuid <UUID>",
"Delete a scene object by uuid", HandleDeleteObject); "Delete a scene object by uuid",
HandleDeleteObject);
m_console.Commands.AddCommand( m_console.Commands.AddCommand(
"Objects", false, "delete object name", "Objects", false, "delete object name",
"delete object name [--regex] <name>", "delete object name [--regex] <name>",
"Delete a scene object by name.", "Delete a scene object by name.",
"If --regex is specified then the name is treatead as a regular expression", "Command will ask for confirmation before proceeding.\n"
+ "If --regex is specified then the name is treatead as a regular expression",
HandleDeleteObject); HandleDeleteObject);
m_console.Commands.AddCommand( m_console.Commands.AddCommand(
"Objects", false, "delete object outside", "Objects", false, "delete object outside",
"delete object outside", "delete object outside",
"Delete all scene objects outside region boundaries", HandleDeleteObject); "Delete all scene objects outside region boundaries",
"Command will ask for confirmation before proceeding.",
HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects",
false,
"delete object pos",
"delete object pos <start-coord> to <end-coord>",
"Delete scene objects within the given area.",
"Each component of the coord is comma separated. There must be no spaces between the commas.\n"
+ "If you don't care about the z component you can simply omit it.\n"
+ "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n"
+ "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n"
+ "If you want to specify the minimum value of a component then you can use -~ instead of a number\n"
+ "e.g.\n"
+ "delete object pos 20,20,20 to 40,40,40\n"
+ "delete object pos 20,20 to 40,40\n"
+ "delete object pos ,20,20 to ,40,40\n"
+ "delete object pos ,,30 to ,,~\n"
+ "delete object pos ,,-~ to ,,30",
HandleDeleteObject);
m_console.Commands.AddCommand( m_console.Commands.AddCommand(
"Objects", "Objects",
false, false,
"show object uuid", "show object uuid",
"show object uuid <UUID>", "show object uuid [--full] <UUID>",
"Show details of a scene object with the given UUID", HandleShowObjectByUuid); "Show details of a scene object with the given UUID",
"The --full option will print out information on all the parts of the object.\n"
+ "For yet more detailed part information, use the \"show part\" commands.",
HandleShowObjectByUuid);
m_console.Commands.AddCommand( m_console.Commands.AddCommand(
"Objects", "Objects",
false, false,
"show object name", "show object name",
"show object name [--regex] <name>", "show object name [--full] [--regex] <name>",
"Show details of scene objects with the given name.", "Show details of scene objects with the given name.",
"If --regex is specified then the name is treatead as a regular expression", "The --full option will print out information on all the parts of the object.\n"
+ "For yet more detailed part information, use the \"show part\" commands.\n"
+ "If --regex is specified then the name is treatead as a regular expression.",
HandleShowObjectByName); HandleShowObjectByName);
m_console.Commands.AddCommand( m_console.Commands.AddCommand(
"Objects", "Objects",
false, false,
"show object pos", "show object pos",
"show object pos <start-coord> to <end-coord>", "show object pos [--full] <start-coord> to <end-coord>",
"Show details of scene objects within the given area.", "Show details of scene objects within the given area.",
"Each component of the coord is comma separated. There must be no spaces between the commas.\n" "The --full option will print out information on all the parts of the object.\n"
+ "For yet more detailed part information, use the \"show part\" commands.\n"
+ "Each component of the coord is comma separated. There must be no spaces between the commas.\n"
+ "If you don't care about the z component you can simply omit it.\n" + "If you don't care about the z component you can simply omit it.\n"
+ "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n" + "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n"
+ "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n" + "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n"
@ -188,7 +223,12 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
// m_log.DebugFormat("[OBJECTS COMMANDS MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName); // m_log.DebugFormat("[OBJECTS COMMANDS MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName);
} }
private void OutputSogsToConsole(Predicate<SceneObjectGroup> searchPredicate) /// <summary>
/// Outputs the sogs to console.
/// </summary>
/// <param name='searchPredicate'></param>
/// <param name='showFull'>If true then output all part details. If false then output summary.</param>
private void OutputSogsToConsole(Predicate<SceneObjectGroup> searchPredicate, bool showFull)
{ {
List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups().FindAll(searchPredicate); List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups().FindAll(searchPredicate);
@ -196,7 +236,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
foreach (SceneObjectGroup so in sceneObjects) foreach (SceneObjectGroup so in sceneObjects)
{ {
AddSceneObjectReport(sb, so); AddSceneObjectReport(sb, so, showFull);
sb.Append("\n"); sb.Append("\n");
} }
@ -205,7 +245,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
m_console.OutputFormat(sb.ToString()); m_console.OutputFormat(sb.ToString());
} }
private void OutputSopsToConsole(Predicate<SceneObjectPart> searchPredicate) private void OutputSopsToConsole(Predicate<SceneObjectPart> searchPredicate, bool showFull)
{ {
List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups(); List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups();
List<SceneObjectPart> parts = new List<SceneObjectPart>(); List<SceneObjectPart> parts = new List<SceneObjectPart>();
@ -216,7 +256,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
foreach (SceneObjectPart part in parts) foreach (SceneObjectPart part in parts)
{ {
AddScenePartReport(sb, part); AddScenePartReport(sb, part, showFull);
sb.Append("\n"); sb.Append("\n");
} }
@ -225,21 +265,26 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
m_console.OutputFormat(sb.ToString()); m_console.OutputFormat(sb.ToString());
} }
private void HandleShowObjectByUuid(string module, string[] cmd) private void HandleShowObjectByUuid(string module, string[] cmdparams)
{ {
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return; return;
if (cmd.Length < 4) bool showFull = false;
OptionSet options = new OptionSet().Add("full", v => showFull = v != null );
List<string> mainParams = options.Parse(cmdparams);
if (mainParams.Count < 4)
{ {
m_console.OutputFormat("Usage: show object uuid <uuid>"); m_console.OutputFormat("Usage: show object uuid <uuid>");
return; return;
} }
UUID objectUuid; UUID objectUuid;
if (!UUID.TryParse(cmd[3], out objectUuid)) if (!UUID.TryParse(mainParams[3], out objectUuid))
{ {
m_console.OutputFormat("{0} is not a valid uuid", cmd[3]); m_console.OutputFormat("{0} is not a valid uuid", mainParams[3]);
return; return;
} }
@ -252,7 +297,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
AddSceneObjectReport(sb, so); AddSceneObjectReport(sb, so, showFull);
m_console.OutputFormat(sb.ToString()); m_console.OutputFormat(sb.ToString());
} }
@ -262,14 +307,17 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return; return;
bool showFull = false;
bool useRegex = false; bool useRegex = false;
OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null ); OptionSet options = new OptionSet();
options.Add("full", v => showFull = v != null );
options.Add("regex", v => useRegex = v != null );
List<string> mainParams = options.Parse(cmdparams); List<string> mainParams = options.Parse(cmdparams);
if (mainParams.Count < 4) if (mainParams.Count < 4)
{ {
m_console.OutputFormat("Usage: show object name [--regex] <name>"); m_console.OutputFormat("Usage: show object name [--full] [--regex] <name>");
return; return;
} }
@ -287,7 +335,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
searchPredicate = so => so.Name == name; searchPredicate = so => so.Name == name;
} }
OutputSogsToConsole(searchPredicate); OutputSogsToConsole(searchPredicate, showFull);
} }
private void HandleShowObjectByPos(string module, string[] cmdparams) private void HandleShowObjectByPos(string module, string[] cmdparams)
@ -295,51 +343,49 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return; return;
if (cmdparams.Length < 5) bool showFull = false;
OptionSet options = new OptionSet().Add("full", v => showFull = v != null );
List<string> mainParams = options.Parse(cmdparams);
if (mainParams.Count < 5)
{ {
m_console.OutputFormat("Usage: show object pos <start-coord> to <end-coord>"); m_console.OutputFormat("Usage: show object pos [--full] <start-coord> to <end-coord>");
return; return;
} }
string rawConsoleStartVector = cmdparams[3]; Vector3 startVector, endVector;
Vector3 startVector;
if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector)) if (!TryParseVectorRange(cmdparams.Skip(3).Take(3), out startVector, out endVector))
{
m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector);
return; return;
}
string rawConsoleEndVector = cmdparams[5];
Vector3 endVector;
if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector))
{
m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector);
return;
}
Predicate<SceneObjectGroup> searchPredicate Predicate<SceneObjectGroup> searchPredicate
= so => Util.IsInsideBox(so.AbsolutePosition, startVector, endVector); = so => Util.IsInsideBox(so.AbsolutePosition, startVector, endVector);
OutputSogsToConsole(searchPredicate); OutputSogsToConsole(searchPredicate, showFull);
} }
private void HandleShowPartByUuid(string module, string[] cmd) private void HandleShowPartByUuid(string module, string[] cmdparams)
{ {
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return; return;
if (cmd.Length < 4) // bool showFull = false;
OptionSet options = new OptionSet();
// options.Add("full", v => showFull = v != null );
List<string> mainParams = options.Parse(cmdparams);
if (mainParams.Count < 4)
{ {
m_console.OutputFormat("Usage: show part uuid <uuid>"); m_console.OutputFormat("Usage: show part uuid [--full] <uuid>");
return; return;
} }
UUID objectUuid; UUID objectUuid;
if (!UUID.TryParse(cmd[3], out objectUuid)) if (!UUID.TryParse(mainParams[3], out objectUuid))
{ {
m_console.OutputFormat("{0} is not a valid uuid", cmd[3]); m_console.OutputFormat("{0} is not a valid uuid", mainParams[3]);
return; return;
} }
@ -352,7 +398,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
AddScenePartReport(sb, sop); AddScenePartReport(sb, sop, true);
m_console.OutputFormat(sb.ToString()); m_console.OutputFormat(sb.ToString());
} }
@ -362,13 +408,19 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return; return;
if (cmdparams.Length < 5) // bool showFull = false;
OptionSet options = new OptionSet();
// options.Add("full", v => showFull = v != null );
List<string> mainParams = options.Parse(cmdparams);
if (mainParams.Count < 5)
{ {
m_console.OutputFormat("Usage: show part pos <start-coord> to <end-coord>"); m_console.OutputFormat("Usage: show part pos [--full] <start-coord> to <end-coord>");
return; return;
} }
string rawConsoleStartVector = cmdparams[3]; string rawConsoleStartVector = mainParams[3];
Vector3 startVector; Vector3 startVector;
if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector)) if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector))
@ -377,7 +429,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
return; return;
} }
string rawConsoleEndVector = cmdparams[5]; string rawConsoleEndVector = mainParams[5];
Vector3 endVector; Vector3 endVector;
if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector)) if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector))
@ -386,7 +438,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
return; return;
} }
OutputSopsToConsole(sop => Util.IsInsideBox(sop.AbsolutePosition, startVector, endVector)); OutputSopsToConsole(sop => Util.IsInsideBox(sop.AbsolutePosition, startVector, endVector), true);
} }
private void HandleShowPartByName(string module, string[] cmdparams) private void HandleShowPartByName(string module, string[] cmdparams)
@ -394,14 +446,17 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return; return;
// bool showFull = false;
bool useRegex = false; bool useRegex = false;
OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null ); OptionSet options = new OptionSet();
// options.Add("full", v => showFull = v != null );
options.Add("regex", v => useRegex = v != null );
List<string> mainParams = options.Parse(cmdparams); List<string> mainParams = options.Parse(cmdparams);
if (mainParams.Count < 4) if (mainParams.Count < 4)
{ {
m_console.OutputFormat("Usage: show part name [--regex] <name>"); m_console.OutputFormat("Usage: show part name [--full] [--regex] <name>");
return; return;
} }
@ -419,31 +474,112 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
searchPredicate = sop => sop.Name == name; searchPredicate = sop => sop.Name == name;
} }
OutputSopsToConsole(searchPredicate); OutputSopsToConsole(searchPredicate, true);
} }
private StringBuilder AddSceneObjectReport(StringBuilder sb, SceneObjectGroup so) /// <summary>
/// Append a scene object report to an input StringBuilder
/// </summary>
/// <returns></returns>
/// <param name='sb'></param>
/// <param name='so'</param>
/// <param name='showFull'>
/// If true then information on all parts of an object is appended.
/// If false then only summary information about an object is appended.
/// </param>
private StringBuilder AddSceneObjectReport(StringBuilder sb, SceneObjectGroup so, bool showFull)
{ {
sb.AppendFormat("Name: {0}\n", so.Name); if (showFull)
sb.AppendFormat("Description: {0}\n", so.Description); {
sb.AppendFormat("Location: {0} @ {1}\n", so.AbsolutePosition, so.Scene.RegionInfo.RegionName); foreach (SceneObjectPart sop in so.Parts)
sb.AppendFormat("Parts: {0}\n", so.PrimCount); {
sb.AppendFormat("Flags: {0}\n", so.RootPart.Flags); AddScenePartReport(sb, sop, false);
sb.Append("\n");
}
}
else
{
AddSummarySceneObjectReport(sb, so);
}
return sb; return sb;
} }
private StringBuilder AddScenePartReport(StringBuilder sb, SceneObjectPart sop) private StringBuilder AddSummarySceneObjectReport(StringBuilder sb, SceneObjectGroup so)
{ {
sb.AppendFormat("Name: {0}\n", sop.Name); ConsoleDisplayList cdl = new ConsoleDisplayList();
sb.AppendFormat("Description: {0}\n", sop.Description); cdl.AddRow("Name", so.Name);
sb.AppendFormat("Location: {0} @ {1}\n", sop.AbsolutePosition, sop.ParentGroup.Scene.RegionInfo.RegionName); cdl.AddRow("Descrition", so.Description);
sb.AppendFormat("Parent: {0}", cdl.AddRow("Local ID", so.LocalId);
sop.IsRoot ? "Is Root\n" : string.Format("{0} {1}\n", sop.ParentGroup.Name, sop.ParentGroup.UUID)); cdl.AddRow("UUID", so.UUID);
sb.AppendFormat("Link number: {0}\n", sop.LinkNum); cdl.AddRow("Location", string.Format("{0} @ {1}", so.AbsolutePosition, so.Scene.Name));
sb.AppendFormat("Flags: {0}\n", sop.Flags); cdl.AddRow("Parts", so.PrimCount);
cdl.AddRow("Flags", so.RootPart.Flags);
return sb; return sb.Append(cdl.ToString());
}
/// <summary>
/// Append a scene object part report to an input StringBuilder
/// </summary>
/// <returns></returns>
/// <param name='sb'></param>
/// <param name='sop'</param>
/// <param name='showFull'>
/// If true then information on each inventory item will be shown.
/// If false then only summary inventory information is shown.
/// </param>
private StringBuilder AddScenePartReport(StringBuilder sb, SceneObjectPart sop, bool showFull)
{
ConsoleDisplayList cdl = new ConsoleDisplayList();
cdl.AddRow("Name", sop.Name);
cdl.AddRow("Description", sop.Description);
cdl.AddRow("Local ID", sop.LocalId);
cdl.AddRow("UUID", sop.UUID);
cdl.AddRow("Location", string.Format("{0} @ {1}", sop.AbsolutePosition, sop.ParentGroup.Scene.Name));
cdl.AddRow(
"Parent",
sop.IsRoot ? "Is Root" : string.Format("{0} {1}", sop.ParentGroup.Name, sop.ParentGroup.UUID));
cdl.AddRow("Link number", sop.LinkNum);
cdl.AddRow("Flags", sop.Flags);
object itemsOutput;
if (showFull)
{
StringBuilder itemsSb = new StringBuilder("\n");
itemsOutput = AddScenePartItemsReport(itemsSb, sop.Inventory).ToString();
}
else
{
itemsOutput = sop.Inventory.Count;
}
cdl.AddRow("Items", itemsOutput);
return sb.Append(cdl.ToString());
}
private StringBuilder AddScenePartItemsReport(StringBuilder sb, IEntityInventory inv)
{
ConsoleDisplayTable cdt = new ConsoleDisplayTable();
cdt.Indent = 2;
cdt.AddColumn("Name", 50);
cdt.AddColumn("Type", 12);
cdt.AddColumn("Running", 7);
cdt.AddColumn("Item UUID", 36);
cdt.AddColumn("Asset UUID", 36);
foreach (TaskInventoryItem item in inv.GetInventoryItems())
cdt.AddRow(
item.Name,
((InventoryType)item.InvType).ToString(),
(InventoryType)item.InvType == InventoryType.LSL ? item.ScriptRunning.ToString() : "n/a",
item.ItemID.ToString(),
item.AssetID.ToString());
return sb.Append(cdt.ToString());
} }
private void HandleDeleteObject(string module, string[] cmd) private void HandleDeleteObject(string module, string[] cmd)
@ -557,6 +693,10 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
break; break;
case "pos":
deletes = GetDeleteCandidatesByPos(module, cmd);
break;
default: default:
m_console.OutputFormat("Unrecognized mode {0}", mode); m_console.OutputFormat("Unrecognized mode {0}", mode);
return; return;
@ -571,7 +711,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
string.Format( string.Format(
"Are you sure that you want to delete {0} objects from {1}", "Are you sure that you want to delete {0} objects from {1}",
deletes.Count, m_scene.RegionInfo.RegionName), deletes.Count, m_scene.RegionInfo.RegionName),
"n"); "y/N");
if (response.ToLower() != "y") if (response.ToLower() != "y")
{ {
@ -593,9 +733,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
private List<SceneObjectGroup> GetDeleteCandidatesByName(string module, string[] cmdparams) private List<SceneObjectGroup> GetDeleteCandidatesByName(string module, string[] cmdparams)
{ {
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return null;
bool useRegex = false; bool useRegex = false;
OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null ); OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null );
@ -629,5 +766,52 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
return sceneObjects; return sceneObjects;
} }
/// <summary>
/// Get scene object delete candidates by position
/// </summary>
/// <param name='module'></param>
/// <param name='cmdparams'></param>
/// <returns>null if parsing failed on one of the arguments, otherwise a list of objects to delete. If there
/// are no objects to delete then the list will be empty./returns>
private List<SceneObjectGroup> GetDeleteCandidatesByPos(string module, string[] cmdparams)
{
if (cmdparams.Length < 5)
{
m_console.OutputFormat("Usage: delete object pos <start-coord> to <end-coord>");
return null;
}
Vector3 startVector, endVector;
if (!TryParseVectorRange(cmdparams.Skip(3).Take(3), out startVector, out endVector))
return null;
return m_scene.GetSceneObjectGroups().FindAll(
so => !so.IsAttachment && Util.IsInsideBox(so.AbsolutePosition, startVector, endVector));
}
private bool TryParseVectorRange(IEnumerable<string> rawComponents, out Vector3 startVector, out Vector3 endVector)
{
string rawConsoleStartVector = rawComponents.Take(1).Single();
if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector))
{
m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector);
endVector = Vector3.Zero;
return false;
}
string rawConsoleEndVector = rawComponents.Skip(1).Take(1).Single();
if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector))
{
m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector);
return false;
}
return true;
}
} }
} }

View File

@ -267,18 +267,26 @@ namespace OpenSim.Region.Framework.Interfaces
void ApplyGodPermissions(uint perms); void ApplyGodPermissions(uint perms);
/// <summary>
/// Number of items in this inventory.
/// </summary>
int Count { get; }
/// <summary> /// <summary>
/// Returns true if this inventory contains any scripts /// Returns true if this inventory contains any scripts
/// </summary></returns> /// </summary></returns>
bool ContainsScripts(); bool ContainsScripts();
/// <summary> /// <summary>
/// Returns the count of scripts contained /// Number of scripts in this inventory.
/// </summary></returns> /// </summary>
/// <remarks>
/// Includes both running and non running scripts.
/// </remarks>
int ScriptCount(); int ScriptCount();
/// <summary> /// <summary>
/// Returns the count of running scripts contained /// Number of running scripts in this inventory.
/// </summary></returns> /// </summary></returns>
int RunningScriptCount(); int RunningScriptCount();

View File

@ -121,13 +121,21 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is triggered for both child and root agent client connections. /// This is triggered for both child and root agent client connections.
///
/// Triggered before OnClientLogin. /// Triggered before OnClientLogin.
///
/// This is triggered under per-agent lock. So if you want to perform any long-running operations, please
/// do this on a separate thread.
/// </remarks> /// </remarks>
public event OnNewClientDelegate OnNewClient; public event OnNewClientDelegate OnNewClient;
/// <summary> /// <summary>
/// Fired if the client entering this sim is doing so as a new login /// Fired if the client entering this sim is doing so as a new login
/// </summary> /// </summary>
/// <remarks>
/// This is triggered under per-agent lock. So if you want to perform any long-running operations, please
/// do this on a separate thread.
/// </remarks>
public event Action<IClientAPI> OnClientLogin; public event Action<IClientAPI> OnClientLogin;
public delegate void OnNewPresenceDelegate(ScenePresence presence); public delegate void OnNewPresenceDelegate(ScenePresence presence);
@ -149,6 +157,9 @@ namespace OpenSim.Region.Framework.Scenes
/// <remarks> /// <remarks>
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.AddNewClient"/> which is used by both /// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.AddNewClient"/> which is used by both
/// <see cref="OpenSim.Framework.PresenceType.User">users</see> and <see cref="OpenSim.Framework.PresenceType.Npc">NPCs</see> /// <see cref="OpenSim.Framework.PresenceType.User">users</see> and <see cref="OpenSim.Framework.PresenceType.Npc">NPCs</see>
///
/// Triggered under per-agent lock. So if you want to perform any long-running operations, please
/// do this on a separate thread.
/// </remarks> /// </remarks>
public event OnRemovePresenceDelegate OnRemovePresence; public event OnRemovePresenceDelegate OnRemovePresence;
@ -425,6 +436,9 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// At the point of firing, the scene still contains the client's scene presence. /// At the point of firing, the scene still contains the client's scene presence.
///
/// This is triggered under per-agent lock. So if you want to perform any long-running operations, please
/// do this on a separate thread.
/// </remarks> /// </remarks>
public event ClientClosed OnClientClosed; public event ClientClosed OnClientClosed;

View File

@ -79,6 +79,11 @@ namespace OpenSim.Region.Framework.Scenes
public SynchronizeSceneHandler SynchronizeScene; public SynchronizeSceneHandler SynchronizeScene;
/// <summary>
/// Used to prevent simultaneous calls to RemoveClient() for the same agent from interfering with each other.
/// </summary>
private object m_removeClientLock = new object();
/// <summary> /// <summary>
/// Statistical information for this scene. /// Statistical information for this scene.
/// </summary> /// </summary>
@ -301,6 +306,31 @@ namespace OpenSim.Region.Framework.Scenes
} }
private volatile bool m_shuttingDown; private volatile bool m_shuttingDown;
/// <summary>
/// Is the scene active?
/// </summary>
/// <remarks>
/// If false, maintenance and update loops are not being run. Updates can still be triggered manually if
/// the scene is not active.
/// </remarks>
public bool Active
{
get { return m_active; }
set
{
if (value)
{
if (!m_active)
Start();
}
else
{
m_active = false;
}
}
}
private volatile bool m_active;
// private int m_lastUpdate; // private int m_lastUpdate;
// private bool m_firstHeartbeat = true; // private bool m_firstHeartbeat = true;
@ -1154,6 +1184,14 @@ namespace OpenSim.Region.Framework.Scenes
public void SetSceneCoreDebug(Dictionary<string, string> options) public void SetSceneCoreDebug(Dictionary<string, string> options)
{ {
if (options.ContainsKey("active"))
{
bool active;
if (bool.TryParse(options["active"], out active))
Active = active;
}
if (options.ContainsKey("scripting")) if (options.ContainsKey("scripting"))
{ {
bool enableScripts = true; bool enableScripts = true;
@ -1293,6 +1331,8 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary> /// </summary>
public void Start() public void Start()
{ {
m_active = true;
// m_log.DebugFormat("[SCENE]: Starting Heartbeat timer for {0}", RegionInfo.RegionName); // m_log.DebugFormat("[SCENE]: Starting Heartbeat timer for {0}", RegionInfo.RegionName);
//m_heartbeatTimer.Enabled = true; //m_heartbeatTimer.Enabled = true;
@ -1334,7 +1374,7 @@ namespace OpenSim.Region.Framework.Scenes
#region Update Methods #region Update Methods
/// <summary> /// <summary>
/// Performs per-frame updates regularly /// Activate the various loops necessary to continually update the scene.
/// </summary> /// </summary>
private void Heartbeat() private void Heartbeat()
{ {
@ -1391,7 +1431,7 @@ namespace OpenSim.Region.Framework.Scenes
List<Vector3> coarseLocations; List<Vector3> coarseLocations;
List<UUID> avatarUUIDs; List<UUID> avatarUUIDs;
while (!m_shuttingDown && (endRun == null || MaintenanceRun < endRun)) while (!m_shuttingDown && ((endRun == null && Active) || MaintenanceRun < endRun))
{ {
runtc = Util.EnvironmentTickCount(); runtc = Util.EnvironmentTickCount();
++MaintenanceRun; ++MaintenanceRun;
@ -1450,7 +1490,7 @@ namespace OpenSim.Region.Framework.Scenes
int previousFrameTick, tmpMS; int previousFrameTick, tmpMS;
int maintc = Util.EnvironmentTickCount(); int maintc = Util.EnvironmentTickCount();
while (!m_shuttingDown && (endFrame == null || Frame < endFrame)) while (!m_shuttingDown && ((endFrame == null && Active) || Frame < endFrame))
{ {
++Frame; ++Frame;
@ -2709,69 +2749,89 @@ namespace OpenSim.Region.Framework.Scenes
public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type)
{ {
ScenePresence sp;
bool vialogin;
// Validation occurs in LLUDPServer // Validation occurs in LLUDPServer
//
// XXX: A race condition exists here where two simultaneous calls to AddNewClient can interfere with
// each other. In practice, this does not currently occur in the code.
AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode); AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode);
bool vialogin // We lock here on AgentCircuitData to prevent a race condition between the thread adding a new connection
= (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 // and a simultaneous one that removes it (as can happen if the client is closed at a particular point
|| (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0; // whilst connecting).
//
// CheckHeartbeat(); // It would be easier to lock across all NewUserConnection(), AddNewClient() and
// RemoveClient() calls for all agents, but this would allow a slow call (e.g. because of slow service
ScenePresence sp = GetScenePresence(client.AgentId); // response in some module listening to AddNewClient()) from holding up unrelated agent calls.
//
// XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this // In practice, the lock (this) in LLUDPServer.AddNewClient() currently lock across all
// could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause // AddNewClient() operations (though not other ops).
// other problems, and possible the code calling AddNewClient() should ensure that no client is already // In the future this can be relieved once locking per agent (not necessarily on AgentCircuitData) is improved.
// connected. lock (aCircuit)
if (sp == null)
{ {
m_log.DebugFormat( vialogin
"[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}", = (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0
client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos); || (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0;
// CheckHeartbeat();
sp = GetScenePresence(client.AgentId);
m_clientManager.Add(client); // XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this
SubscribeToClientEvents(client); // could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause
// other problems, and possible the code calling AddNewClient() should ensure that no client is already
sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type); // connected.
m_eventManager.TriggerOnNewPresence(sp); if (sp == null)
sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
// The first agent upon login is a root agent by design.
// For this agent we will have to rez the attachments.
// All other AddNewClient calls find aCircuit.child to be true.
if (aCircuit.child == false)
{ {
// We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to m_log.DebugFormat(
// start the scripts again (since this is done in RezAttachments()). "[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}",
// XXX: This is convoluted. client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos);
sp.IsChildAgent = false;
m_clientManager.Add(client);
if (AttachmentsModule != null) SubscribeToClientEvents(client);
Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); });
sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
m_eventManager.TriggerOnNewPresence(sp);
sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
// The first agent upon login is a root agent by design.
// For this agent we will have to rez the attachments.
// All other AddNewClient calls find aCircuit.child to be true.
if (aCircuit.child == false)
{
// We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to
// start the scripts again (since this is done in RezAttachments()).
// XXX: This is convoluted.
sp.IsChildAgent = false;
if (AttachmentsModule != null)
Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); });
}
} }
} else
else {
{ m_log.WarnFormat(
m_log.WarnFormat( "[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence",
"[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence", sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName);
sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName); }
}
// We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
// client is for a root or child agent.
client.SceneAgent = sp;
// We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the // Cache the user's name
// client is for a root or child agent. CacheUserName(sp, aCircuit);
client.SceneAgent = sp;
EventManager.TriggerOnNewClient(client);
if (vialogin)
EventManager.TriggerOnClientLogin(client);
}
m_LastLogin = Util.EnvironmentTickCount(); m_LastLogin = Util.EnvironmentTickCount();
// Cache the user's name
CacheUserName(sp, aCircuit);
EventManager.TriggerOnNewClient(client);
if (vialogin)
EventManager.TriggerOnClientLogin(client);
return sp; return sp;
} }
@ -3300,109 +3360,129 @@ namespace OpenSim.Region.Framework.Scenes
{ {
// CheckHeartbeat(); // CheckHeartbeat();
bool isChildAgent = false; bool isChildAgent = false;
ScenePresence avatar = GetScenePresence(agentID); AgentCircuitData acd;
if (avatar == null) lock (m_removeClientLock)
{ {
m_log.WarnFormat( acd = m_authenticateHandler.GetAgentCircuitData(agentID);
"[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID);
return; if (acd == null)
{
m_log.ErrorFormat("[SCENE]: No agent circuit found for {0}, aborting Scene.RemoveClient", agentID);
return;
}
else
{
// We remove the acd up here to avoid later raec conditions if two RemoveClient() calls occurred
// simultaneously.
m_authenticateHandler.RemoveCircuit(acd.circuitcode);
}
} }
try lock (acd)
{ {
isChildAgent = avatar.IsChildAgent; ScenePresence avatar = GetScenePresence(agentID);
m_log.DebugFormat( if (avatar == null)
"[SCENE]: Removing {0} agent {1} {2} from {3}",
(isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName);
// Don't do this to root agents, it's not nice for the viewer
if (closeChildAgents && isChildAgent)
{ {
// Tell a single agent to disconnect from the region. m_log.WarnFormat(
IEventQueue eq = RequestModuleInterface<IEventQueue>(); "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID);
if (eq != null)
{ return;
eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID);
}
else
{
avatar.ControllingClient.SendShutdownConnectionNotice();
}
} }
// Only applies to root agents.
if (avatar.ParentID != 0)
{
avatar.StandUp();
}
m_sceneGraph.removeUserCount(!isChildAgent);
// TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop
// unnecessary operations. This should go away once NPCs have no accompanying IClientAPI
if (closeChildAgents && CapsModule != null)
CapsModule.RemoveCaps(agentID);
// REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever
// this method is doing is HORRIBLE!!!
avatar.Scene.NeedSceneCacheClear(avatar.UUID);
if (closeChildAgents && !isChildAgent)
{
List<ulong> regions = avatar.KnownRegionHandles;
regions.Remove(RegionInfo.RegionHandle);
m_sceneGridService.SendCloseChildAgentConnections(agentID, regions);
}
m_eventManager.TriggerClientClosed(agentID, this);
m_eventManager.TriggerOnRemovePresence(agentID);
if (!isChildAgent)
{
if (AttachmentsModule != null)
{
AttachmentsModule.DeRezAttachments(avatar);
}
ForEachClient(
delegate(IClientAPI client)
{
//We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway
try { client.SendKillObject(avatar.RegionHandle, new List<uint> { avatar.LocalId }); }
catch (NullReferenceException) { }
});
}
// It's possible for child agents to have transactions if changes are being made cross-border.
if (AgentTransactionsModule != null)
AgentTransactionsModule.RemoveAgentAssetTransactions(agentID);
m_authenticateHandler.RemoveCircuit(avatar.ControllingClient.CircuitCode);
}
catch (Exception e)
{
m_log.Error(
string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e);
}
finally
{
try try
{ {
// Always clean these structures up so that any failure above doesn't cause them to remain in the isChildAgent = avatar.IsChildAgent;
// scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering
// the same cleanup exception continually. m_log.DebugFormat(
m_sceneGraph.RemoveScenePresence(agentID); "[SCENE]: Removing {0} agent {1} {2} from {3}",
m_clientManager.Remove(agentID); (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName);
avatar.Close(); // Don't do this to root agents, it's not nice for the viewer
if (closeChildAgents && isChildAgent)
{
// Tell a single agent to disconnect from the region.
IEventQueue eq = RequestModuleInterface<IEventQueue>();
if (eq != null)
{
eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID);
}
else
{
avatar.ControllingClient.SendShutdownConnectionNotice();
}
}
// Only applies to root agents.
if (avatar.ParentID != 0)
{
avatar.StandUp();
}
m_sceneGraph.removeUserCount(!isChildAgent);
// TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop
// unnecessary operations. This should go away once NPCs have no accompanying IClientAPI
if (closeChildAgents && CapsModule != null)
CapsModule.RemoveCaps(agentID);
// REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever
// this method is doing is HORRIBLE!!!
avatar.Scene.NeedSceneCacheClear(avatar.UUID);
if (closeChildAgents && !isChildAgent)
{
List<ulong> regions = avatar.KnownRegionHandles;
regions.Remove(RegionInfo.RegionHandle);
m_sceneGridService.SendCloseChildAgentConnections(agentID, regions);
}
m_eventManager.TriggerClientClosed(agentID, this);
m_eventManager.TriggerOnRemovePresence(agentID);
if (!isChildAgent)
{
if (AttachmentsModule != null)
{
AttachmentsModule.DeRezAttachments(avatar);
}
ForEachClient(
delegate(IClientAPI client)
{
//We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway
try { client.SendKillObject(avatar.RegionHandle, new List<uint> { avatar.LocalId }); }
catch (NullReferenceException) { }
});
}
// It's possible for child agents to have transactions if changes are being made cross-border.
if (AgentTransactionsModule != null)
AgentTransactionsModule.RemoveAgentAssetTransactions(agentID);
} }
catch (Exception e) catch (Exception e)
{ {
m_log.Error( m_log.Error(
string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e); string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e);
}
finally
{
try
{
// Always clean these structures up so that any failure above doesn't cause them to remain in the
// scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering
// the same cleanup exception continually.
m_sceneGraph.RemoveScenePresence(agentID);
m_clientManager.Remove(agentID);
avatar.Close();
}
catch (Exception e)
{
m_log.Error(
string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e);
}
} }
} }
@ -3461,11 +3541,9 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary> /// <summary>
/// Do the work necessary to initiate a new user connection for a particular scene. /// Do the work necessary to initiate a new user connection for a particular scene.
/// At the moment, this consists of setting up the caps infrastructure
/// The return bool should allow for connections to be refused, but as not all calling paths
/// take proper notice of it let, we allowed banned users in still.
/// </summary> /// </summary>
/// <param name="agent">CircuitData of the agent who is connecting</param> /// <param name="agent">CircuitData of the agent who is connecting</param>
/// <param name="teleportFlags"></param>
/// <param name="reason">Outputs the reason for the false response on this string</param> /// <param name="reason">Outputs the reason for the false response on this string</param>
/// <returns>True if the region accepts this agent. False if it does not. False will /// <returns>True if the region accepts this agent. False if it does not. False will
/// also return a reason.</returns> /// also return a reason.</returns>
@ -3476,10 +3554,20 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary> /// <summary>
/// Do the work necessary to initiate a new user connection for a particular scene. /// Do the work necessary to initiate a new user connection for a particular scene.
/// At the moment, this consists of setting up the caps infrastructure /// </summary>
/// <remarks>
/// The return bool should allow for connections to be refused, but as not all calling paths
/// take proper notice of it yet, we still allowed banned users in.
///
/// At the moment this method consists of setting up the caps infrastructure
/// The return bool should allow for connections to be refused, but as not all calling paths /// The return bool should allow for connections to be refused, but as not all calling paths
/// take proper notice of it let, we allowed banned users in still. /// take proper notice of it let, we allowed banned users in still.
/// </summary> ///
/// This method is called by the login service (in the case of login) or another simulator (in the case of region
/// cross or teleport) to initiate the connection. It is not triggered by the viewer itself - the connection
/// is activated later when the viewer sends the initial UseCircuitCodePacket UDP packet (in the case of
/// the LLUDP stack).
/// </remarks>
/// <param name="agent">CircuitData of the agent who is connecting</param> /// <param name="agent">CircuitData of the agent who is connecting</param>
/// <param name="reason">Outputs the reason for the false response on this string</param> /// <param name="reason">Outputs the reason for the false response on this string</param>
/// <param name="requirePresenceLookup">True for normal presence. False for NPC /// <param name="requirePresenceLookup">True for normal presence. False for NPC
@ -3564,88 +3652,97 @@ namespace OpenSim.Region.Framework.Scenes
agent.firstname, agent.lastname, agent.Viewer); agent.firstname, agent.lastname, agent.Viewer);
reason = "Access denied, your viewer is banned by the region owner"; reason = "Access denied, your viewer is banned by the region owner";
return false; return false;
}
ScenePresence sp = GetScenePresence(agent.AgentID);
if (sp != null && !sp.IsChildAgent)
{
// We have a zombie from a crashed session.
// Or the same user is trying to be root twice here, won't work.
// Kill it.
m_log.WarnFormat(
"[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.",
sp.Name, sp.UUID, RegionInfo.RegionName);
sp.ControllingClient.Close(true);
sp = null;
} }
ILandObject land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); ILandObject land;
//On login test land permisions lock (agent)
if (vialogin)
{ {
if (land != null && !TestLandRestrictions(agent, land, out reason)) ScenePresence sp = GetScenePresence(agent.AgentID);
if (sp != null && !sp.IsChildAgent)
{ {
return false; // We have a zombie from a crashed session.
// Or the same user is trying to be root twice here, won't work.
// Kill it.
m_log.WarnFormat(
"[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.",
sp.Name, sp.UUID, RegionInfo.RegionName);
sp.ControllingClient.Close(true);
sp = null;
} }
}
land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y);
if (sp == null) // We don't have an [child] agent here already
{ //On login test land permisions
if (requirePresenceLookup) if (vialogin)
{ {
try if (land != null && !TestLandRestrictions(agent, land, out reason))
{ {
if (!VerifyUserPresence(agent, out reason))
return false;
} catch (Exception e)
{
m_log.ErrorFormat(
"[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace);
return false; return false;
} }
} }
try if (sp == null) // We don't have an [child] agent here already
{ {
if (!AuthorizeUser(agent, out reason)) if (requirePresenceLookup)
{
try
{
if (!VerifyUserPresence(agent, out reason))
return false;
}
catch (Exception e)
{
m_log.ErrorFormat(
"[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace);
return false;
}
}
try
{
if (!AuthorizeUser(agent, out reason))
return false;
}
catch (Exception e)
{
m_log.ErrorFormat(
"[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace);
return false; return false;
} catch (Exception e) }
{
m_log.ErrorFormat( m_log.InfoFormat(
"[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace); "[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})",
return false; RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname,
} agent.AgentID, agent.circuitcode);
m_log.InfoFormat(
"[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})",
RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname,
agent.AgentID, agent.circuitcode);
if (CapsModule != null)
{
CapsModule.SetAgentCapsSeeds(agent);
CapsModule.CreateCaps(agent.AgentID);
}
} else
{
// Let the SP know how we got here. This has a lot of interesting
// uses down the line.
sp.TeleportFlags = (TPFlags)teleportFlags;
if (sp.IsChildAgent)
{
m_log.DebugFormat(
"[SCENE]: Adjusting known seeds for existing agent {0} in {1}",
agent.AgentID, RegionInfo.RegionName);
sp.AdjustKnownSeeds();
if (CapsModule != null) if (CapsModule != null)
{
CapsModule.SetAgentCapsSeeds(agent); CapsModule.SetAgentCapsSeeds(agent);
CapsModule.CreateCaps(agent.AgentID);
}
}
else
{
// Let the SP know how we got here. This has a lot of interesting
// uses down the line.
sp.TeleportFlags = (TPFlags)teleportFlags;
if (sp.IsChildAgent)
{
m_log.DebugFormat(
"[SCENE]: Adjusting known seeds for existing agent {0} in {1}",
agent.AgentID, RegionInfo.RegionName);
sp.AdjustKnownSeeds();
if (CapsModule != null)
CapsModule.SetAgentCapsSeeds(agent);
}
} }
} }
@ -4047,8 +4144,9 @@ namespace OpenSim.Region.Framework.Scenes
return false; return false;
} }
// We have to wait until the viewer contacts this region after receiving EAC. // We have to wait until the viewer contacts this region
// That calls AddNewClient, which finally creates the ScenePresence // after receiving the EnableSimulator HTTP Event Queue message. This triggers the viewer to send
// a UseCircuitCode packet which in turn calls AddNewClient which finally creates the ScenePresence.
ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID); ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID);
if (childAgentUpdate != null) if (childAgentUpdate != null)

View File

@ -3432,6 +3432,7 @@ namespace OpenSim.Region.Framework.Scenes
/// <remarks> /// <remarks>
/// When the physics engine has finished with it, the sculpt data is discarded to save memory. /// When the physics engine has finished with it, the sculpt data is discarded to save memory.
/// </remarks> /// </remarks>
/*
public void CheckSculptAndLoad() public void CheckSculptAndLoad()
{ {
if (IsDeleted) if (IsDeleted)
@ -3447,7 +3448,7 @@ namespace OpenSim.Region.Framework.Scenes
for (int i = 0; i < parts.Length; i++) for (int i = 0; i < parts.Length; i++)
parts[i].CheckSculptAndLoad(); parts[i].CheckSculptAndLoad();
} }
*/
/// <summary> /// <summary>
/// Set the user group to which this scene object belongs. /// Set the user group to which this scene object belongs.
/// </summary> /// </summary>

View File

@ -1014,9 +1014,9 @@ namespace OpenSim.Region.Framework.Scenes
{ {
actor.Size = m_shape.Scale; actor.Size = m_shape.Scale;
if (Shape.SculptEntry) // if (Shape.SculptEntry)
CheckSculptAndLoad(); // CheckSculptAndLoad();
else // else
ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor); ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor);
} }
} }
@ -1620,12 +1620,13 @@ namespace OpenSim.Region.Framework.Scenes
if (userExposed) if (userExposed)
{ {
/*
if (dupe.m_shape.SculptEntry && dupe.m_shape.SculptTexture != UUID.Zero) if (dupe.m_shape.SculptEntry && dupe.m_shape.SculptTexture != UUID.Zero)
{ {
ParentGroup.Scene.AssetService.Get( ParentGroup.Scene.AssetService.Get(
dupe.m_shape.SculptTexture.ToString(), dupe, dupe.AssetReceived); dupe.m_shape.SculptTexture.ToString(), dupe, dupe.AssetReceived);
} }
*/
bool UsePhysics = ((dupe.Flags & PrimFlags.Physics) != 0); bool UsePhysics = ((dupe.Flags & PrimFlags.Physics) != 0);
dupe.DoPhysicsPropertyUpdate(UsePhysics, true); dupe.DoPhysicsPropertyUpdate(UsePhysics, true);
} }
@ -1643,6 +1644,7 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="id">ID of asset received</param> /// <param name="id">ID of asset received</param>
/// <param name="sender">Register</param> /// <param name="sender">Register</param>
/// <param name="asset"></param> /// <param name="asset"></param>
/*
protected void AssetReceived(string id, Object sender, AssetBase asset) protected void AssetReceived(string id, Object sender, AssetBase asset)
{ {
if (asset != null) if (asset != null)
@ -1652,7 +1654,7 @@ namespace OpenSim.Region.Framework.Scenes
"[SCENE OBJECT PART]: Part {0} {1} requested mesh/sculpt data for asset id {2} from asset service but received no data", "[SCENE OBJECT PART]: Part {0} {1} requested mesh/sculpt data for asset id {2} from asset service but received no data",
Name, UUID, id); Name, UUID, id);
} }
*/
/// <summary> /// <summary>
/// Do a physics property update for a NINJA joint. /// Do a physics property update for a NINJA joint.
/// </summary> /// </summary>
@ -1833,9 +1835,9 @@ namespace OpenSim.Region.Framework.Scenes
// If this part is a sculpt then delay the physics update until we've asynchronously loaded the // If this part is a sculpt then delay the physics update until we've asynchronously loaded the
// mesh data. // mesh data.
if (Shape.SculptEntry) // if (Shape.SculptEntry)
CheckSculptAndLoad(); // CheckSculptAndLoad();
else // else
ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa); ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa);
} }
} }
@ -2511,6 +2513,7 @@ namespace OpenSim.Region.Framework.Scenes
/// Set sculpt and mesh data, and tell the physics engine to process the change. /// Set sculpt and mesh data, and tell the physics engine to process the change.
/// </summary> /// </summary>
/// <param name="texture">The mesh itself.</param> /// <param name="texture">The mesh itself.</param>
/*
public void SculptTextureCallback(AssetBase texture) public void SculptTextureCallback(AssetBase texture)
{ {
if (m_shape.SculptEntry) if (m_shape.SculptEntry)
@ -2538,7 +2541,7 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
} }
*/
/// <summary> /// <summary>
/// Send a full update to the client for the given part /// Send a full update to the client for the given part
/// </summary> /// </summary>
@ -3783,7 +3786,7 @@ namespace OpenSim.Region.Framework.Scenes
public void UpdateExtraParam(ushort type, bool inUse, byte[] data) public void UpdateExtraParam(ushort type, bool inUse, byte[] data)
{ {
m_shape.ReadInUpdateExtraParam(type, inUse, data); m_shape.ReadInUpdateExtraParam(type, inUse, data);
/*
if (type == 0x30) if (type == 0x30)
{ {
if (m_shape.SculptEntry && m_shape.SculptTexture != UUID.Zero) if (m_shape.SculptEntry && m_shape.SculptTexture != UUID.Zero)
@ -3791,7 +3794,7 @@ namespace OpenSim.Region.Framework.Scenes
ParentGroup.Scene.AssetService.Get(m_shape.SculptTexture.ToString(), this, AssetReceived); ParentGroup.Scene.AssetService.Get(m_shape.SculptTexture.ToString(), this, AssetReceived);
} }
} }
*/
if (ParentGroup != null) if (ParentGroup != null)
{ {
ParentGroup.HasGroupChanged = true; ParentGroup.HasGroupChanged = true;
@ -4025,14 +4028,6 @@ namespace OpenSim.Region.Framework.Scenes
if (!wasUsingPhysics) if (!wasUsingPhysics)
{ {
DoPhysicsPropertyUpdate(UsePhysics, false); DoPhysicsPropertyUpdate(UsePhysics, false);
if (!ParentGroup.IsDeleted)
{
if (LocalId == ParentGroup.RootPart.LocalId)
{
ParentGroup.CheckSculptAndLoad();
}
}
} }
} }
else else
@ -4072,14 +4067,6 @@ namespace OpenSim.Region.Framework.Scenes
pa.SetMaterial(Material); pa.SetMaterial(Material);
DoPhysicsPropertyUpdate(UsePhysics, true); DoPhysicsPropertyUpdate(UsePhysics, true);
if (!ParentGroup.IsDeleted)
{
if (LocalId == ParentGroup.RootPart.LocalId)
{
ParentGroup.CheckSculptAndLoad();
}
}
if ( if (
((AggregateScriptEvents & scriptEvents.collision) != 0) || ((AggregateScriptEvents & scriptEvents.collision) != 0) ||
((AggregateScriptEvents & scriptEvents.collision_end) != 0) || ((AggregateScriptEvents & scriptEvents.collision_end) != 0) ||
@ -4104,14 +4091,6 @@ namespace OpenSim.Region.Framework.Scenes
else // it already has a physical representation else // it already has a physical representation
{ {
DoPhysicsPropertyUpdate(UsePhysics, false); // Update physical status. If it's phantom this will remove the prim DoPhysicsPropertyUpdate(UsePhysics, false); // Update physical status. If it's phantom this will remove the prim
if (!ParentGroup.IsDeleted)
{
if (LocalId == ParentGroup.RootPart.LocalId)
{
ParentGroup.CheckSculptAndLoad();
}
}
} }
} }
@ -4341,6 +4320,7 @@ namespace OpenSim.Region.Framework.Scenes
/// <remarks> /// <remarks>
/// When the physics engine has finished with it, the sculpt data is discarded to save memory. /// When the physics engine has finished with it, the sculpt data is discarded to save memory.
/// </remarks> /// </remarks>
/*
public void CheckSculptAndLoad() public void CheckSculptAndLoad()
{ {
// m_log.DebugFormat("Processing CheckSculptAndLoad for {0} {1}", Name, LocalId); // m_log.DebugFormat("Processing CheckSculptAndLoad for {0} {1}", Name, LocalId);
@ -4366,7 +4346,7 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
} }
*/
/// <summary> /// <summary>
/// Update the texture entry for this part. /// Update the texture entry for this part.
/// </summary> /// </summary>
@ -4604,6 +4584,7 @@ namespace OpenSim.Region.Framework.Scenes
} }
Quaternion rot = Quaternion.Slerp(RotationOffset,APIDTarget,1.0f/(float)m_APIDIterations); Quaternion rot = Quaternion.Slerp(RotationOffset,APIDTarget,1.0f/(float)m_APIDIterations);
rot.Normalize();
UpdateRotation(rot); UpdateRotation(rot);
m_APIDIterations--; m_APIDIterations--;

View File

@ -92,6 +92,15 @@ namespace OpenSim.Region.Framework.Scenes
QueryScriptStates(); QueryScriptStates();
} }
} }
public int Count
{
get
{
lock (m_items)
return m_items.Count;
}
}
/// <summary> /// <summary>
/// Constructor /// Constructor

View File

@ -66,9 +66,9 @@ namespace OpenSim.Region.Framework.Scenes
// /// </summary> // /// </summary>
// private bool m_waitingForObjectAsset; // private bool m_waitingForObjectAsset;
public UuidGatherer(IAssetService assetCache) public UuidGatherer(IAssetService assetService)
{ {
m_assetService = assetCache; m_assetService = assetService;
} }
/// <summary> /// <summary>

View File

@ -146,7 +146,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
sb.AppendFormat("Attachments for {0}\n", sp.Name); sb.AppendFormat("Attachments for {0}\n", sp.Name);
ConsoleDisplayTable ct = new ConsoleDisplayTable() { Indent = 2 }; ConsoleDisplayTable ct = new ConsoleDisplayTable() { Indent = 2 };
ct.Columns.Add(new ConsoleDisplayTableColumn("Attachment Name", 36)); ct.Columns.Add(new ConsoleDisplayTableColumn("Attachment Name", 50));
ct.Columns.Add(new ConsoleDisplayTableColumn("Local ID", 10)); ct.Columns.Add(new ConsoleDisplayTableColumn("Local ID", 10));
ct.Columns.Add(new ConsoleDisplayTableColumn("Item ID", 36)); ct.Columns.Add(new ConsoleDisplayTableColumn("Item ID", 36));
ct.Columns.Add(new ConsoleDisplayTableColumn("Attach Point", 14)); ct.Columns.Add(new ConsoleDisplayTableColumn("Attach Point", 14));

View File

@ -27,6 +27,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using log4net; using log4net;
using Mono.Addins; using Mono.Addins;
@ -36,6 +37,8 @@ using OpenMetaverse.StructuredData;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
{ {
@ -45,6 +48,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private List<Scene> m_sceneList = new List<Scene>(); private List<Scene> m_sceneList = new List<Scene>();
private IPresenceService m_presenceService;
private IMessageTransferModule m_msgTransferModule = null; private IMessageTransferModule m_msgTransferModule = null;
@ -54,6 +58,27 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
private bool m_groupMessagingEnabled = false; private bool m_groupMessagingEnabled = false;
private bool m_debugEnabled = true; private bool m_debugEnabled = true;
/// <summary>
/// If enabled, module only tries to send group IMs to online users by querying cached presence information.
/// </summary>
private bool m_messageOnlineAgentsOnly;
/// <summary>
/// Cache for online users.
/// </summary>
/// <remarks>
/// Group ID is key, presence information for online members is value.
/// Will only be non-null if m_messageOnlineAgentsOnly = true
/// We cache here so that group messages don't constantly have to re-request the online user list to avoid
/// attempted expensive sending of messages to offline users.
/// The tradeoff is that a user that comes online will not receive messages consistently from all other users
/// until caches have updated.
/// Therefore, we set the cache expiry to just 20 seconds.
/// </remarks>
private ExpiringCache<UUID, PresenceInfo[]> m_usersOnlineCache;
private int m_usersOnlineCacheExpirySeconds = 20;
#region IRegionModuleBase Members #region IRegionModuleBase Members
public void Initialise(IConfigSource config) public void Initialise(IConfigSource config)
@ -83,10 +108,17 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return; return;
} }
m_messageOnlineAgentsOnly = groupsConfig.GetBoolean("MessageOnlineUsersOnly", false);
if (m_messageOnlineAgentsOnly)
m_usersOnlineCache = new ExpiringCache<UUID, PresenceInfo[]>();
m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true); m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true);
} }
m_log.Info("[GROUPS-MESSAGING]: GroupsMessagingModule starting up"); m_log.InfoFormat(
"[GROUPS-MESSAGING]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}",
m_messageOnlineAgentsOnly, m_debugEnabled);
} }
public void AddRegion(Scene scene) public void AddRegion(Scene scene)
@ -126,6 +158,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return; return;
} }
if (m_presenceService == null)
m_presenceService = scene.PresenceService;
m_sceneList.Add(scene); m_sceneList.Add(scene);
@ -207,12 +241,42 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
public void SendMessageToGroup(GridInstantMessage im, UUID groupID) public void SendMessageToGroup(GridInstantMessage im, UUID groupID)
{ {
List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID), groupID); List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID), groupID);
int groupMembersCount = groupMembers.Count;
if (m_debugEnabled)
m_log.DebugFormat( if (m_messageOnlineAgentsOnly)
"[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members", {
groupID, groupMembers.Count); string[] t1 = groupMembers.ConvertAll<string>(gmd => gmd.AgentID.ToString()).ToArray();
// We cache in order not to overwhlem the presence service on large grids with many groups. This does
// mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed.
// (assuming this is the same across all grid simulators).
PresenceInfo[] onlineAgents;
if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents))
{
onlineAgents = m_presenceService.GetAgents(t1);
m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds);
}
HashSet<string> onlineAgentsUuidSet = new HashSet<string>();
Array.ForEach<PresenceInfo>(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID));
groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList();
// if (m_debugEnabled)
// m_log.DebugFormat(
// "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members, {2} online",
// groupID, groupMembersCount, groupMembers.Count());
}
else
{
if (m_debugEnabled)
m_log.DebugFormat(
"[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members",
groupID, groupMembers.Count);
}
int requestStartTick = Environment.TickCount;
foreach (GroupMembersData member in groupMembers) foreach (GroupMembersData member in groupMembers)
{ {
if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID)) if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID))
@ -254,6 +318,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
ProcessMessageFromGroupSession(msg); ProcessMessageFromGroupSession(msg);
} }
} }
// Temporary for assessing how long it still takes to send messages to large online groups.
if (m_messageOnlineAgentsOnly)
m_log.DebugFormat(
"[GROUPS-MESSAGING]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms",
groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick);
} }
#region SimGridEventHandlers #region SimGridEventHandlers

View File

@ -123,7 +123,36 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
public void AddRegion(Scene scene) public void AddRegion(Scene scene)
{ {
if (m_groupsEnabled) if (m_groupsEnabled)
{
scene.RegisterModuleInterface<IGroupsModule>(this); scene.RegisterModuleInterface<IGroupsModule>(this);
scene.AddCommand(
"debug",
this,
"debug groups verbose",
"debug groups verbose <true|false>",
"This setting turns on very verbose groups debugging",
HandleDebugGroupsVerbose);
}
}
private void HandleDebugGroupsVerbose(object modules, string[] args)
{
if (args.Length < 4)
{
MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
return;
}
bool verbose = false;
if (!bool.TryParse(args[3], out verbose))
{
MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
return;
}
m_debugEnabled = verbose;
MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled);
} }
public void RegionLoaded(Scene scene) public void RegionLoaded(Scene scene)

View File

@ -41,8 +41,6 @@ public class BSCharacter : BSPhysObject
// private bool _stopped; // private bool _stopped;
private OMV.Vector3 _size; private OMV.Vector3 _size;
private OMV.Vector3 _scale;
private PrimitiveBaseShape _pbs;
private bool _grabbed; private bool _grabbed;
private bool _selected; private bool _selected;
private OMV.Vector3 _position; private OMV.Vector3 _position;
@ -67,6 +65,10 @@ public class BSCharacter : BSPhysObject
private bool _kinematic; private bool _kinematic;
private float _buoyancy; private float _buoyancy;
// The friction and velocity of the avatar is modified depending on whether walking or not.
private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar
private float _currentFriction; // the friction currently being used (changed by setVelocity).
private OMV.Vector3 _PIDTarget; private OMV.Vector3 _PIDTarget;
private bool _usePID; private bool _usePID;
private float _PIDTau; private float _PIDTau;
@ -84,14 +86,18 @@ public class BSCharacter : BSPhysObject
_flying = isFlying; _flying = isFlying;
_orientation = OMV.Quaternion.Identity; _orientation = OMV.Quaternion.Identity;
_velocity = OMV.Vector3.Zero; _velocity = OMV.Vector3.Zero;
_appliedVelocity = OMV.Vector3.Zero;
_buoyancy = ComputeBuoyancyFromFlying(isFlying); _buoyancy = ComputeBuoyancyFromFlying(isFlying);
_currentFriction = PhysicsScene.Params.avatarStandingFriction;
_avatarDensity = PhysicsScene.Params.avatarDensity;
// The dimensions of the avatar capsule are kept in the scale. // The dimensions of the avatar capsule are kept in the scale.
// Physics creates a unit capsule which is scaled by the physics engine. // Physics creates a unit capsule which is scaled by the physics engine.
ComputeAvatarScale(_size); ComputeAvatarScale(_size);
_avatarDensity = PhysicsScene.Params.avatarDensity; // set _avatarVolume and _mass based on capsule size, _density and Scale
// set _avatarVolume and _mass based on capsule size, _density and _scale
ComputeAvatarVolumeAndMass(); ComputeAvatarVolumeAndMass();
DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
LocalID, _size, Scale, _avatarDensity, _avatarVolume, MassRaw);
ShapeData shapeData = new ShapeData(); ShapeData shapeData = new ShapeData();
shapeData.ID = LocalID; shapeData.ID = LocalID;
@ -99,28 +105,22 @@ public class BSCharacter : BSPhysObject
shapeData.Position = _position; shapeData.Position = _position;
shapeData.Rotation = _orientation; shapeData.Rotation = _orientation;
shapeData.Velocity = _velocity; shapeData.Velocity = _velocity;
shapeData.Scale = _scale; shapeData.Size = Scale;
shapeData.Scale = Scale;
shapeData.Mass = _mass; shapeData.Mass = _mass;
shapeData.Buoyancy = _buoyancy; shapeData.Buoyancy = _buoyancy;
shapeData.Static = ShapeData.numericFalse; shapeData.Static = ShapeData.numericFalse;
shapeData.Friction = PhysicsScene.Params.avatarFriction; shapeData.Friction = PhysicsScene.Params.avatarStandingFriction;
shapeData.Restitution = PhysicsScene.Params.avatarRestitution; shapeData.Restitution = PhysicsScene.Params.avatarRestitution;
// do actual create at taint time // do actual create at taint time
PhysicsScene.TaintedObject("BSCharacter.create", delegate() PhysicsScene.TaintedObject("BSCharacter.create", delegate()
{ {
DetailLog("{0},BSCharacter.create,taint", LocalID); DetailLog("{0},BSCharacter.create,taint", LocalID);
BulletSimAPI.CreateObject(PhysicsScene.WorldID, shapeData); // New body and shape into BSBody and BSShape
PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this, shapeData, null, null, null);
// Set the buoyancy for flying. This will be refactored when all the settings happen in C#. SetPhysicalProperties();
// If not set at creation, the avatar will stop flying when created after crossing a region boundry.
BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy);
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(PhysicsScene.World.ptr, LocalID));
// This works here because CreateObject has already put the character into the physical world.
BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr,
(uint)CollisionFilterGroups.AvatarFilter, (uint)CollisionFilterGroups.AvatarMask);
}); });
return; return;
} }
@ -131,53 +131,85 @@ public class BSCharacter : BSPhysObject
DetailLog("{0},BSCharacter.Destroy", LocalID); DetailLog("{0},BSCharacter.Destroy", LocalID);
PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
{ {
BulletSimAPI.DestroyObject(PhysicsScene.WorldID, LocalID); PhysicsScene.Shapes.DereferenceBody(BSBody, true, null);
PhysicsScene.Shapes.DereferenceShape(BSShape, true, null);
}); });
} }
private void SetPhysicalProperties()
{
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, BSBody.ptr);
ZeroMotion();
ForcePosition = _position;
// Set the velocity and compute the proper friction
ForceVelocity = _velocity;
BulletSimAPI.SetRestitution2(BSBody.ptr, PhysicsScene.Params.avatarRestitution);
BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale);
BulletSimAPI.SetContactProcessingThreshold2(BSBody.ptr, PhysicsScene.Params.contactProcessingThreshold);
if (PhysicsScene.Params.ccdMotionThreshold > 0f)
{
BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
}
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw);
BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia);
BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT);
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, BSBody.ptr);
BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, BSBody.ptr);
// Do this after the object has been added to the world
BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr,
(uint)CollisionFilterGroups.AvatarFilter,
(uint)CollisionFilterGroups.AvatarMask);
}
public override void RequestPhysicsterseUpdate() public override void RequestPhysicsterseUpdate()
{ {
base.RequestPhysicsterseUpdate(); base.RequestPhysicsterseUpdate();
} }
// No one calls this method so I don't know what it could possibly mean // No one calls this method so I don't know what it could possibly mean
public override bool Stopped { public override bool Stopped { get { return false; } }
get { return false; }
}
public override OMV.Vector3 Size { public override OMV.Vector3 Size {
get get
{ {
// Avatar capsule size is kept in the scale parameter. // Avatar capsule size is kept in the scale parameter.
return new OMV.Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z); return _size;
} }
set { set {
// When an avatar's size is set, only the height is changed // When an avatar's size is set, only the height is changed.
// and that really only depends on the radius.
_size = value; _size = value;
ComputeAvatarScale(_size); ComputeAvatarScale(_size);
// TODO: something has to be done with the avatar's vertical position
ComputeAvatarVolumeAndMass(); ComputeAvatarVolumeAndMass();
DetailLog("{0},BSCharacter.setSize,call,scale={1},density={2},volume={3},mass={4}",
LocalID, Scale, _avatarDensity, _avatarVolume, MassRaw);
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
{ {
BulletSimAPI.SetObjectScaleMass(PhysicsScene.WorldID, LocalID, _scale, _mass, true); BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale);
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw);
BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia);
}); });
} }
} }
public override PrimitiveBaseShape Shape { public override OMV.Vector3 Scale { get; set; }
set { _pbs = value; public override PrimitiveBaseShape Shape
} {
set { BaseShape = value; }
} }
public override bool Grabbed { public override bool Grabbed {
set { _grabbed = value; set { _grabbed = value; }
}
} }
public override bool Selected { public override bool Selected {
set { _selected = value; set { _selected = value; }
}
} }
public override void CrossingFailure() { return; } public override void CrossingFailure() { return; }
public override void link(PhysicsActor obj) { return; } public override void link(PhysicsActor obj) { return; }
@ -204,7 +236,7 @@ public class BSCharacter : BSPhysObject
public override OMV.Vector3 Position { public override OMV.Vector3 Position {
get { get {
// _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID); // _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
return _position; return _position;
} }
set { set {
@ -214,7 +246,7 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
{ {
DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
}); });
} }
} }
@ -263,7 +295,7 @@ public class BSCharacter : BSPhysObject
// A version of the sanity check that also makes sure a new position value is // A version of the sanity check that also makes sure a new position value is
// pushed back to the physics engine. This routine would be used by anyone // pushed back to the physics engine. This routine would be used by anyone
// who is not already pushing the value. // who is not already pushing the value.
private bool PositionSanityCheck2(bool atTaintTime) private bool PositionSanityCheck(bool inTaintTime)
{ {
bool ret = false; bool ret = false;
if (PositionSanityCheck()) if (PositionSanityCheck())
@ -273,9 +305,9 @@ public class BSCharacter : BSPhysObject
BSScene.TaintCallback sanityOperation = delegate() BSScene.TaintCallback sanityOperation = delegate()
{ {
DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
}; };
if (atTaintTime) if (inTaintTime)
sanityOperation(); sanityOperation();
else else
PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", sanityOperation); PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", sanityOperation);
@ -284,11 +316,7 @@ public class BSCharacter : BSPhysObject
return ret; return ret;
} }
public override float Mass { public override float Mass { get { return _mass; } }
get {
return _mass;
}
}
// used when we only want this prim's mass and not the linkset thing // used when we only want this prim's mass and not the linkset thing
public override float MassRaw { get {return _mass; } } public override float MassRaw { get {return _mass; } }
@ -301,15 +329,13 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
{ {
DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
BulletSimAPI.SetObjectForce(PhysicsScene.WorldID, LocalID, _force); BulletSimAPI.SetObjectForce2(BSBody.ptr, _force);
}); });
} }
} }
public override int VehicleType { // Avatars don't do vehicles
get { return 0; } public override int VehicleType { get { return 0; } set { return; } }
set { return; }
}
public override void VehicleFloatParam(int param, float value) { } public override void VehicleFloatParam(int param, float value) { }
public override void VehicleVectorParam(int param, OMV.Vector3 value) {} public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { } public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
@ -328,10 +354,39 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
{ {
DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity); DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
BulletSimAPI.SetObjectVelocity(PhysicsScene.WorldID, LocalID, _velocity); ForceVelocity = _velocity;
}); });
} }
} }
public override OMV.Vector3 ForceVelocity {
get { return _velocity; }
set {
// Depending on whether the avatar is moving or not, change the friction
// to keep the avatar from slipping around
if (_velocity.Length() == 0)
{
if (_currentFriction != PhysicsScene.Params.avatarStandingFriction)
{
_currentFriction = PhysicsScene.Params.avatarStandingFriction;
BulletSimAPI.SetFriction2(BSBody.ptr, _currentFriction);
}
}
else
{
if (_currentFriction != PhysicsScene.Params.avatarFriction)
{
_currentFriction = PhysicsScene.Params.avatarFriction;
BulletSimAPI.SetFriction2(BSBody.ptr, _currentFriction);
}
}
_velocity = value;
// Remember the set velocity so we can suppress the reduction by friction, ...
_appliedVelocity = value;
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity);
BulletSimAPI.Activate2(BSBody.ptr, true);
}
}
public override OMV.Vector3 Torque { public override OMV.Vector3 Torque {
get { return _torque; } get { return _torque; }
set { _torque = value; set { _torque = value;
@ -353,8 +408,8 @@ public class BSCharacter : BSPhysObject
// m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
{ {
// _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID); // _position = BulletSimAPI.GetPosition2(BSBody.ptr);
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
}); });
} }
} }
@ -382,12 +437,18 @@ public class BSCharacter : BSPhysObject
set { _isPhysical = value; set { _isPhysical = value;
} }
} }
public override bool IsSolid {
get { return true; }
}
public override bool IsStatic {
get { return false; }
}
public override bool Flying { public override bool Flying {
get { return _flying; } get { return _flying; }
set { set {
_flying = value; _flying = value;
// simulate flying by changing the effect of gravity // simulate flying by changing the effect of gravity
this.Buoyancy = ComputeBuoyancyFromFlying(_flying); Buoyancy = ComputeBuoyancyFromFlying(_flying);
} }
} }
// Flying is implimented by changing the avatar's buoyancy. // Flying is implimented by changing the avatar's buoyancy.
@ -432,6 +493,10 @@ public class BSCharacter : BSPhysObject
get { return _rotationalVelocity; } get { return _rotationalVelocity; }
set { _rotationalVelocity = value; } set { _rotationalVelocity = value; }
} }
public override OMV.Vector3 ForceRotationalVelocity {
get { return _rotationalVelocity; }
set { _rotationalVelocity = value; }
}
public override bool Kinematic { public override bool Kinematic {
get { return _kinematic; } get { return _kinematic; }
set { _kinematic = value; } set { _kinematic = value; }
@ -443,10 +508,19 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate() PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
{ {
DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy); ForceBuoyancy = _buoyancy;
}); });
} }
} }
public override float ForceBuoyancy {
get { return _buoyancy; }
set { _buoyancy = value;
DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
// Buoyancy is faked by changing the gravity applied to the object
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav));
}
}
// Used for MoveTo // Used for MoveTo
public override OMV.Vector3 PIDTarget { public override OMV.Vector3 PIDTarget {
@ -507,27 +581,32 @@ public class BSCharacter : BSPhysObject
private void ComputeAvatarScale(OMV.Vector3 size) private void ComputeAvatarScale(OMV.Vector3 size)
{ {
_scale.X = PhysicsScene.Params.avatarCapsuleRadius; // The 'size' given by the simulator is the mid-point of the avatar
_scale.Y = PhysicsScene.Params.avatarCapsuleRadius; // and X and Y are unspecified.
// The 1.15 came from ODE but it seems to cause the avatar to float off the ground OMV.Vector3 newScale = OMV.Vector3.Zero;
// _scale.Z = (_size.Z * 1.15f) - (_scale.X + _scale.Y); newScale.X = PhysicsScene.Params.avatarCapsuleRadius;
_scale.Z = (_size.Z) - (_scale.X + _scale.Y); newScale.Y = PhysicsScene.Params.avatarCapsuleRadius;
// From the total height, remote the capsule half spheres that are at each end
newScale.Z = (size.Z * 2f) - Math.Min(newScale.X, newScale.Y);
// newScale.Z = (size.Z * 2f);
Scale = newScale;
} }
// set _avatarVolume and _mass based on capsule size, _density and _scale // set _avatarVolume and _mass based on capsule size, _density and Scale
private void ComputeAvatarVolumeAndMass() private void ComputeAvatarVolumeAndMass()
{ {
_avatarVolume = (float)( _avatarVolume = (float)(
Math.PI Math.PI
* _scale.X * Scale.X
* _scale.Y // the area of capsule cylinder * Scale.Y // the area of capsule cylinder
* _scale.Z // times height of capsule cylinder * Scale.Z // times height of capsule cylinder
+ 1.33333333f + 1.33333333f
* Math.PI * Math.PI
* _scale.X * Scale.X
* Math.Min(_scale.X, _scale.Y) * Math.Min(Scale.X, Scale.Y)
* _scale.Y // plus the volume of the capsule end caps * Scale.Y // plus the volume of the capsule end caps
); );
_mass = _avatarDensity * _avatarVolume; _mass = _avatarDensity * _avatarVolume;
} }
@ -542,7 +621,23 @@ public class BSCharacter : BSPhysObject
_acceleration = entprop.Acceleration; _acceleration = entprop.Acceleration;
_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.
PositionSanityCheck2(true); PositionSanityCheck(true);
// remember the current and last set values
LastEntityProperties = CurrentEntityProperties;
CurrentEntityProperties = entprop;
if (entprop.Velocity != LastEntityProperties.Velocity)
{
// Changes in the velocity are suppressed in avatars.
// That's just the way they are defined.
OMV.Vector3 avVel = new OMV.Vector3(_appliedVelocity.X, _appliedVelocity.Y, entprop.Velocity.Z);
_velocity = avVel;
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, avVel);
}
// Tell the linkset about this
Linkset.UpdateProperties(this);
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
// base.RequestPhysicsterseUpdate(); // base.RequestPhysicsterseUpdate();

View File

@ -49,9 +49,16 @@ public abstract class BSConstraint : IDisposable
if (m_enabled) if (m_enabled)
{ {
m_enabled = false; m_enabled = false;
bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr); if (m_constraint.ptr != IntPtr.Zero)
m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success); {
m_constraint.ptr = System.IntPtr.Zero; bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}",
BSScene.DetailLogZero,
m_body1.ID, m_body1.ptr.ToString("X"),
m_body2.ID, m_body2.ptr.ToString("X"),
success);
m_constraint.ptr = System.IntPtr.Zero;
}
} }
} }

View File

@ -462,7 +462,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
return; return;
// Set the prim's inertia to zero. The vehicle code handles that and this // Set the prim's inertia to zero. The vehicle code handles that and this
// removes the torque action introduced by Bullet. // removes the motion and torque actions introduced by Bullet.
Vector3 inertia = Vector3.Zero; Vector3 inertia = Vector3.Zero;
BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia); BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia);
BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr); BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr);
@ -481,7 +481,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_lastPositionVector = Prim.ForcePosition; m_lastPositionVector = Prim.ForcePosition;
VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
Prim.LocalID, Prim.Position, Prim.Force, Prim.Velocity, Prim.RotationalVelocity); Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity);
}// end Step }// end Step
// Apply the effect of the linear motor. // Apply the effect of the linear motor.
@ -540,7 +540,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// add Gravity and Buoyancy // add Gravity and Buoyancy
// There is some gravity, make a gravity force vector that is applied after object velocity. // There is some gravity, make a gravity force vector that is applied after object velocity.
// m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
Vector3 grav = Prim.PhysicsScene.DefaultGravity * (Prim.Mass * (1f - m_VehicleBuoyancy)); Vector3 grav = Prim.PhysicsScene.DefaultGravity * (Prim.Linkset.LinksetMass * (1f - m_VehicleBuoyancy));
/* /*
* RA: Not sure why one would do this * RA: Not sure why one would do this
@ -678,10 +678,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_newVelocity.Z = 0; m_newVelocity.Z = 0;
// Apply velocity // Apply velocity
Prim.Velocity = m_newVelocity; Prim.ForceVelocity = m_newVelocity;
// apply gravity force // apply gravity force
// Why is this set here? The physics engine already does gravity. // Why is this set here? The physics engine already does gravity.
// m_prim.AddForce(grav, false); Prim.AddForce(grav, false, true);
// Apply friction // Apply friction
Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep)); Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep));
@ -704,7 +704,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// m_lastAngularVelocity // what was last applied to body // m_lastAngularVelocity // what was last applied to body
// Get what the body is doing, this includes 'external' influences // Get what the body is doing, this includes 'external' influences
Vector3 angularVelocity = Prim.RotationalVelocity; Vector3 angularVelocity = Prim.ForceRotationalVelocity;
if (m_angularMotorApply > 0) if (m_angularMotorApply > 0)
{ {
@ -810,7 +810,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_lastAngularVelocity -= m_lastAngularVelocity * decayamount; m_lastAngularVelocity -= m_lastAngularVelocity * decayamount;
// Apply to the body // Apply to the body
Prim.RotationalVelocity = m_lastAngularVelocity; Prim.ForceRotationalVelocity = m_lastAngularVelocity;
VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", Prim.LocalID, decayamount, m_lastAngularVelocity); VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", Prim.LocalID, decayamount, m_lastAngularVelocity);
} //end MoveAngular } //end MoveAngular
@ -862,7 +862,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
private void VDetailLog(string msg, params Object[] args) private void VDetailLog(string msg, params Object[] args)
{ {
if (Prim.PhysicsScene.VehicleLoggingEnabled) if (Prim.PhysicsScene.VehicleLoggingEnabled)
Prim.PhysicsScene.PhysicsLogging.Write(msg, args); Prim.PhysicsScene.DetailLog(msg, args);
} }
} }
} }

View File

@ -52,8 +52,8 @@ public class BSLinkset
// the physical 'taint' children separately. // the physical 'taint' children separately.
// After taint processing and before the simulation step, these // After taint processing and before the simulation step, these
// two lists must be the same. // two lists must be the same.
private List<BSPhysObject> m_children; private HashSet<BSPhysObject> m_children;
private List<BSPhysObject> m_taintChildren; private HashSet<BSPhysObject> m_taintChildren;
// We lock the diddling of linkset classes to prevent any badness. // We lock the diddling of linkset classes to prevent any badness.
// This locks the modification of the instances of this class. Changes // This locks the modification of the instances of this class. Changes
@ -90,8 +90,8 @@ public class BSLinkset
m_nextLinksetID = 1; m_nextLinksetID = 1;
PhysicsScene = scene; PhysicsScene = scene;
LinksetRoot = parent; LinksetRoot = parent;
m_children = new List<BSPhysObject>(); m_children = new HashSet<BSPhysObject>();
m_taintChildren = new List<BSPhysObject>(); m_taintChildren = new HashSet<BSPhysObject>();
m_mass = parent.MassRaw; m_mass = parent.MassRaw;
} }
@ -160,6 +160,28 @@ public class BSLinkset
return ret; return ret;
} }
// When physical properties are changed the linkset needs to recalculate
// its internal properties.
// May be called at runtime or taint-time (just pass the appropriate flag).
public void Refresh(BSPhysObject requestor, bool inTaintTime)
{
// If there are no children, not physical or not root, I am not the one that recomputes the constraints
// (For the moment, static linksets do create constraints so remove the test for physical.)
if (!HasAnyChildren || /*!requestor.IsPhysical ||*/ !IsRoot(requestor))
return;
BSScene.TaintCallback refreshOperation = delegate()
{
RecomputeLinksetConstraintVariables();
DetailLog("{0},BSLinkset.Refresh,complete,rBody={1}",
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"));
};
if (inTaintTime)
refreshOperation();
else
PhysicsScene.TaintedObject("BSLinkSet.Refresh", refreshOperation);
}
// The object is going dynamic (physical). Do any setup necessary // The object is going dynamic (physical). Do any setup necessary
// for a dynamic linkset. // for a dynamic linkset.
// Only the state of the passed object can be modified. The rest of the linkset // Only the state of the passed object can be modified. The rest of the linkset
@ -182,22 +204,19 @@ public class BSLinkset
return false; return false;
} }
// When physical properties are changed the linkset needs to recalculate // If the software is handling the movement of all the objects in a linkset
// its internal properties. // (like if one doesn't use constraints for static linksets), this is called
// Called at runtime. // when an update for the root of the linkset is received.
public void Refresh(BSPhysObject requestor) // Called at taint-time!!
public void UpdateProperties(BSPhysObject physObject)
{ {
// If there are no children, there can't be any constraints to recompute // The root local properties have been updated. Apply to the children if appropriate.
if (!HasAnyChildren) if (IsRoot(physObject) && HasAnyChildren)
return;
// Only the root does the recomputation
if (IsRoot(requestor))
{ {
PhysicsScene.TaintedObject("BSLinkSet.Refresh", delegate() if (!physObject.IsPhysical)
{ {
RecomputeLinksetConstraintVariables(); // TODO: implement software linkset update for static object linksets
}); }
} }
} }
@ -215,13 +234,10 @@ public class BSLinkset
if (IsRoot(child)) if (IsRoot(child))
{ {
// If the one with the dependency is root, must undo all children // If the one with the dependency is root, must undo all children
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},numChild={2}", DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"));
foreach (BSPhysObject bpo in m_taintChildren)
{ ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody);
ret = true;
}
} }
else else
{ {
@ -229,20 +245,16 @@ public class BSLinkset
child.LocalID, child.LocalID,
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
child.LocalID, child.BSBody.ptr.ToString("X")); child.LocalID, child.BSBody.ptr.ToString("X"));
// Remove the dependency on the body of this one // ret = PhysicallyUnlinkAChildFromRoot(LinksetRoot, child);
if (m_taintChildren.Contains(child)) // Despite the function name, this removes any link to the specified object.
{ ret = PhysicallyUnlinkAllChildrenFromRoot(child);
PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody);
ret = true;
}
} }
} }
return ret; return ret;
} }
// Routine used when rebuilding the body of the root of the linkset // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
// This is called after RemoveAllLinksToRoot() to restore all the constraints. // this routine will restore the removed constraints.
// This is called when the root body has been changed.
// Called at taint-time!! // Called at taint-time!!
public void RestoreBodyDependencies(BSPrim child) public void RestoreBodyDependencies(BSPrim child)
{ {
@ -254,7 +266,7 @@ public class BSLinkset
child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count);
foreach (BSPhysObject bpo in m_taintChildren) foreach (BSPhysObject bpo in m_taintChildren)
{ {
PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody); PhysicallyLinkAChildToRoot(LinksetRoot, bpo);
} }
} }
else else
@ -263,7 +275,7 @@ public class BSLinkset
LinksetRoot.LocalID, LinksetRoot.LocalID,
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
child.LocalID, child.BSBody.ptr.ToString("X")); child.LocalID, child.BSBody.ptr.ToString("X"));
PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody); PhysicallyLinkAChildToRoot(LinksetRoot, child);
} }
} }
} }
@ -330,22 +342,22 @@ public class BSLinkset
{ {
m_children.Add(child); m_children.Add(child);
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now BSPhysObject rootx = LinksetRoot; // capture the root as of now
BSPhysObject childx = child; BSPhysObject childx = child;
DetailLog("{0},AddChildToLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", DetailLog("{0},AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
rootx.LocalID,
rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
childx.LocalID, childx.BSBody.ptr.ToString("X"));
PhysicsScene.TaintedObject("AddChildToLinkset", delegate() PhysicsScene.TaintedObject("AddChildToLinkset", delegate()
{ {
DetailLog("{0},AddChildToLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID); DetailLog("{0},AddChildToLinkset,taint,rID={1},rBody={2},cID={3},cBody={4}",
// build the physical binding between me and the child rootx.LocalID,
m_taintChildren.Add(childx); rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
childx.LocalID, childx.BSBody.ptr.ToString("X"));
// Since this is taint-time, the body and shape could have changed for the child // Since this is taint-time, the body and shape could have changed for the child
PhysicallyLinkAChildToRoot(rootx, rootx.BSBody, childx, childx.BSBody); rootx.ForcePosition = rootx.Position; // DEBUG
childx.ForcePosition = childx.Position; // DEBUG
PhysicallyLinkAChildToRoot(rootx, childx);
m_taintChildren.Add(child);
}); });
} }
return; return;
@ -378,10 +390,8 @@ public class BSLinkset
PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate()
{ {
if (m_taintChildren.Contains(childx)) m_taintChildren.Remove(child);
m_taintChildren.Remove(childx); PhysicallyUnlinkAChildFromRoot(rootx, childx);
PhysicallyUnlinkAChildFromRoot(rootx, rootx.BSBody, childx, childx.BSBody);
RecomputeLinksetConstraintVariables(); RecomputeLinksetConstraintVariables();
}); });
@ -396,8 +406,7 @@ public class BSLinkset
// Create a constraint between me (root of linkset) and the passed prim (the child). // Create a constraint between me (root of linkset) and the passed prim (the child).
// Called at taint time! // Called at taint time!
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BulletBody rootBody, private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
BSPhysObject childPrim, BulletBody childBody)
{ {
// Zero motion for children so they don't interpolate // Zero motion for children so they don't interpolate
childPrim.ZeroMotion(); childPrim.ZeroMotion();
@ -409,33 +418,17 @@ public class BSLinkset
// real world coordinate of midpoint between the two objects // real world coordinate of midpoint between the two objects
OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
rootPrim.LocalID, rootPrim.LocalID,
rootPrim.LocalID, rootBody.ptr.ToString("X"), rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"),
childPrim.LocalID, childBody.ptr.ToString("X"), childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"),
rootPrim.Position, childPrim.Position, midPoint); rootPrim.Position, childPrim.Position, midPoint);
// create a constraint that allows no freedom of movement between the two objects // create a constraint that allows no freedom of movement between the two objects
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
// There is great subtlty in these paramters. Notice the check for a ptr of zero.
// We pass the BulletBody structure into the taint in order to capture the pointer
// of the body at the time of constraint creation. This doesn't work for the very first
// construction because there is no body yet. The body
// is constructed later at taint time. Thus we use the body address at time of the
// taint creation but, if it is zero, use what's in the prim at the moment.
// There is a possible race condition since shape can change without a taint call
// (like changing to a mesh that is already constructed). The fix for that would be
// to only change BSShape at taint time thus syncronizing these operations at
// the cost of efficiency and lag.
BS6DofConstraint constrain = new BS6DofConstraint( BS6DofConstraint constrain = new BS6DofConstraint(
PhysicsScene.World, PhysicsScene.World, rootPrim.BSBody, childPrim.BSBody, midPoint, true, true );
rootBody.ptr == IntPtr.Zero ? rootPrim.BSBody : rootBody,
childBody.ptr == IntPtr.Zero ? childPrim.BSBody : childBody,
midPoint,
true,
true
);
/* NOTE: below is an attempt to build constraint with full frame computation, etc. /* NOTE: below is an attempt to build constraint with full frame computation, etc.
* Using the midpoint is easier since it lets the Bullet code manipulate the transforms * Using the midpoint is easier since it lets the Bullet code manipulate the transforms
@ -452,7 +445,7 @@ public class BSLinkset
// create a constraint that allows no freedom of movement between the two objects // create a constraint that allows no freedom of movement between the two objects
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID); DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
BS6DofConstraint constrain = new BS6DofConstraint( BS6DofConstraint constrain = new BS6DofConstraint(
PhysicsScene.World, rootPrim.Body, childPrim.Body, PhysicsScene.World, rootPrim.Body, childPrim.Body,
OMV.Vector3.Zero, OMV.Vector3.Zero,
@ -486,39 +479,44 @@ public class BSLinkset
{ {
constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations);
} }
RecomputeLinksetConstraintVariables();
} }
// Remove linkage between myself and a particular child // Remove linkage between myself and a particular child
// The root and child bodies are passed in because we need to remove the constraint between // The root and child bodies are passed in because we need to remove the constraint between
// the bodies that were at unlink time. // the bodies that were at unlink time.
// Called at taint time! // Called at taint time!
private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BulletBody rootBody, private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
BSPhysObject childPrim, BulletBody childBody)
{ {
DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", bool ret = false;
DetailLog("{0},BSLinkset.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
rootPrim.LocalID, rootPrim.LocalID,
rootPrim.LocalID, rootBody.ptr.ToString("X"), rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"),
childPrim.LocalID, childBody.ptr.ToString("X")); childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"));
// Find the constraint for this link and get rid of it from the overall collection and from my list // Find the constraint for this link and get rid of it from the overall collection and from my list
PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootBody, childBody); if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody))
{
// Make the child refresh its location
BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr);
ret = true;
}
// Make the child refresh its location return ret;
BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr);
} }
/*
// Remove linkage between myself and any possible children I might have. // Remove linkage between myself and any possible children I might have.
// Called at taint time! // Called at taint time!
private void PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
{ {
DetailLog("{0},PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); DetailLog("{0},BSLinkset.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
bool ret = false;
PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody); if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody))
{
ret = true;
}
return ret;
} }
*/
// Call each of the constraints that make up this linkset and recompute the // Call each of the constraints that make up this linkset and recompute the
// various transforms and variables. Used when objects are added or removed // various transforms and variables. Used when objects are added or removed
@ -550,11 +548,17 @@ public class BSLinkset
{ {
// If this is a multiple object linkset, set everybody's center of mass to the set's center of mass // If this is a multiple object linkset, set everybody's center of mass to the set's center of mass
OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass(); OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr, centerOfMass, OMV.Quaternion.Identity); BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr,
centerOfMass, OMV.Quaternion.Identity);
DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,setCenterOfMass,COM={1},rBody={2}",
LinksetRoot.LocalID, centerOfMass, LinksetRoot.BSBody.ptr.ToString("X"));
foreach (BSPhysObject child in m_taintChildren) foreach (BSPhysObject child in m_taintChildren)
{ {
BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr, centerOfMass, OMV.Quaternion.Identity); BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr,
centerOfMass, OMV.Quaternion.Identity);
} }
// BulletSimAPI.DumpAllInfo2(PhysicsScene.World.ptr); // DEBUG DEBUG DEBUG
} }
return; return;
} }
@ -563,7 +567,8 @@ public class BSLinkset
// Invoke the detailed logger and output something if it's enabled. // Invoke the detailed logger and output something if it's enabled.
private void DetailLog(string msg, params Object[] args) private void DetailLog(string msg, params Object[] args)
{ {
PhysicsScene.PhysicsLogging.Write(msg, args); if (PhysicsScene.PhysicsLogging.Enabled)
PhysicsScene.DetailLog(msg, args);
} }
} }

View File

@ -47,6 +47,7 @@ public abstract class BSPhysObject : PhysicsActor
TypeName = typeName; TypeName = typeName;
Linkset = new BSLinkset(PhysicsScene, this); Linkset = new BSLinkset(PhysicsScene, this);
LastAssetBuildFailed = false;
CollisionCollection = new CollisionEventUpdate(); CollisionCollection = new CollisionEventUpdate();
SubscribedEventsMs = 0; SubscribedEventsMs = 0;
@ -69,6 +70,23 @@ public abstract class BSPhysObject : PhysicsActor
// Reference to the physical shape (btCollisionShape) of this object // Reference to the physical shape (btCollisionShape) of this object
public BulletShape BSShape; public BulletShape BSShape;
// 'true' if the mesh's underlying asset failed to build.
// This will keep us from looping after the first time the build failed.
public bool LastAssetBuildFailed { get; set; }
// The objects base shape information. Null if not a prim type shape.
public PrimitiveBaseShape BaseShape { get; protected set; }
// When the physical properties are updated, an EntityProperty holds the update values.
// Keep the current and last EntityProperties to enable computation of differences
// between the current update and the previous values.
public EntityProperties CurrentEntityProperties { get; set; }
public EntityProperties LastEntityProperties { get; set; }
public abstract OMV.Vector3 Scale { get; set; }
public abstract bool IsSolid { get; }
public abstract bool IsStatic { get; }
// Stop all physical motion. // Stop all physical motion.
public abstract void ZeroMotion(); public abstract void ZeroMotion();
@ -85,6 +103,14 @@ public abstract class BSPhysObject : PhysicsActor
public abstract OMV.Quaternion ForceOrientation { get; set; } public abstract OMV.Quaternion ForceOrientation { get; set; }
public abstract OMV.Vector3 ForceVelocity { get; set; }
public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
public abstract float ForceBuoyancy { get; set; }
public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
#region Collisions #region Collisions
// Requested number of milliseconds between collision events. Zero means disabled. // Requested number of milliseconds between collision events. Zero means disabled.
@ -125,30 +151,28 @@ public abstract class BSPhysObject : PhysicsActor
// if someone has subscribed for collision events.... // if someone has subscribed for collision events....
if (SubscribedEvents()) { if (SubscribedEvents()) {
CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
// DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}", DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
// LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth); LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
ret = true; ret = true;
} }
return ret; return ret;
} }
// Routine to send the collected collisions into the simulator. // Send the collected collisions into the simulator.
// Also handles removal of this from the collection of objects with collisions if
// there are no collisions from this object. Mechanism is create one last
// collision event to make collision_end work.
// Called at taint time from within the Step() function thus no locking problems // Called at taint time from within the Step() function thus no locking problems
// with CollisionCollection and ObjectsWithNoMoreCollisions. // with CollisionCollection and ObjectsWithNoMoreCollisions.
// Return 'true' if there were some actual collisions passed up // Return 'true' if there were some actual collisions passed up
public virtual bool SendCollisions() public virtual bool SendCollisions()
{ {
bool ret = true; bool ret = true;
// If the 'no collision' call, force it to happen right now so quick collision_end
bool force = CollisionCollection.Count == 0;
// throttle the collisions to the number of milliseconds specified in the subscription // throttle the collisions to the number of milliseconds specified in the subscription
int nowTime = PhysicsScene.SimulationNowTime; if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
if (nowTime >= NextCollisionOkTime)
{ {
NextCollisionOkTime = nowTime + SubscribedEventsMs; NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
// We are called if we previously had collisions. If there are no collisions // We are called if we previously had collisions. If there are no collisions
// this time, send up one last empty event so OpenSim can sense collision end. // this time, send up one last empty event so OpenSim can sense collision end.
@ -207,7 +231,8 @@ public abstract class BSPhysObject : PhysicsActor
// High performance detailed logging routine used by the physical objects. // High performance detailed logging routine used by the physical objects.
protected void DetailLog(string msg, params Object[] args) protected void DetailLog(string msg, params Object[] args)
{ {
PhysicsScene.PhysicsLogging.Write(msg, args); if (PhysicsScene.PhysicsLogging.Enabled)
PhysicsScene.DetailLog(msg, args);
} }
} }
} }

View File

@ -46,12 +46,10 @@ public sealed class BSPrim : BSPhysObject
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[BULLETS PRIM]"; private static readonly string LogHeader = "[BULLETS PRIM]";
private PrimitiveBaseShape _pbs; // _size is what the user passed. Scale is what we pass to the physics engine with the mesh.
// Often Scale is unity because the meshmerizer will apply _size when creating the mesh.
// _size is what the user passed. _scale is what we pass to the physics engine with the mesh.
// Often _scale is unity because the meshmerizer will apply _size when creating the mesh.
private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer // private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer
private bool _grabbed; private bool _grabbed;
private bool _isSelected; private bool _isSelected;
@ -98,12 +96,12 @@ public sealed class BSPrim : BSPhysObject
_physicsActorType = (int)ActorTypes.Prim; _physicsActorType = (int)ActorTypes.Prim;
_position = pos; _position = pos;
_size = size; _size = size;
_scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type Scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type
_orientation = rotation; _orientation = rotation;
_buoyancy = 1f; _buoyancy = 1f;
_velocity = OMV.Vector3.Zero; _velocity = OMV.Vector3.Zero;
_rotationalVelocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero;
_pbs = pbs; BaseShape = pbs;
_isPhysical = pisPhysical; _isPhysical = pisPhysical;
_isVolumeDetect = false; _isVolumeDetect = false;
_friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material _friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material
@ -160,33 +158,32 @@ public sealed class BSPrim : BSPhysObject
get { return _size; } get { return _size; }
set { set {
_size = value; _size = value;
PhysicsScene.TaintedObject("BSPrim.setSize", delegate() ForceBodyShapeRebuild(false);
{
_mass = CalculateMass(); // changing size changes the mass
// Since _size changed, the mesh needs to be rebuilt. If rebuilt, all the correct
// scale and margins are set.
CreateGeomAndObject(true);
// DetailLog("{0},BSPrim.setSize,size={1},scale={2},mass={3},physical={4}", LocalID, _size, _scale, _mass, IsPhysical);
});
} }
} }
// Scale is what we set in the physics engine. It is different than 'size' in that // Scale is what we set in the physics engine. It is different than 'size' in that
// 'size' can be encorporated into the mesh. In that case, the scale is <1,1,1>. // 'size' can be encorporated into the mesh. In that case, the scale is <1,1,1>.
public OMV.Vector3 Scale public override OMV.Vector3 Scale { get; set; }
{
get { return _scale; }
set { _scale = value; }
}
public override PrimitiveBaseShape Shape { public override PrimitiveBaseShape Shape {
set { set {
_pbs = value; BaseShape = value;
PhysicsScene.TaintedObject("BSPrim.setShape", delegate() ForceBodyShapeRebuild(false);
{
_mass = CalculateMass(); // changing the shape changes the mass
CreateGeomAndObject(true);
});
} }
} }
public override bool ForceBodyShapeRebuild(bool inTaintTime)
{
BSScene.TaintCallback rebuildOperation = delegate()
{
_mass = CalculateMass(); // changing the shape changes the mass
CreateGeomAndObject(true);
};
if (inTaintTime)
rebuildOperation();
else
PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", rebuildOperation);
return true;
}
public override bool Grabbed { public override bool Grabbed {
set { _grabbed = value; set { _grabbed = value;
} }
@ -196,7 +193,7 @@ public sealed class BSPrim : BSPhysObject
_isSelected = value; _isSelected = value;
PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() PhysicsScene.TaintedObject("BSPrim.setSelected", delegate()
{ {
// DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
SetObjectDynamic(false); SetObjectDynamic(false);
}); });
} }
@ -265,6 +262,11 @@ public sealed class BSPrim : BSPhysObject
return _position; return _position;
} }
set { set {
// If you must push the position into the physics engine, use ForcePosition.
if (_position == value)
{
return;
}
_position = value; _position = value;
// TODO: what does it mean to set the position of a child prim?? Rebuild the constraint? // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint?
PositionSanityCheck(); PositionSanityCheck();
@ -320,9 +322,9 @@ public sealed class BSPrim : BSPhysObject
} }
// A version of the sanity check that also makes sure a new position value is // A version of the sanity check that also makes sure a new position value is
// pushed back to the physics engine. This routine would be used by anyone // pushed to the physics engine. This routine would be used by anyone
// who is not already pushing the value. // who is not already pushing the value.
private bool PositionSanityCheck2(bool atTaintTime) private bool PositionSanityCheck(bool inTaintTime)
{ {
bool ret = false; bool ret = false;
if (PositionSanityCheck()) if (PositionSanityCheck())
@ -332,9 +334,9 @@ public sealed class BSPrim : BSPhysObject
BSScene.TaintCallback sanityOperation = delegate() BSScene.TaintCallback sanityOperation = delegate()
{ {
DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); ForcePosition = _position;
}; };
if (atTaintTime) if (inTaintTime)
sanityOperation(); sanityOperation();
else else
PhysicsScene.TaintedObject("BSPrim.PositionSanityCheck", sanityOperation); PhysicsScene.TaintedObject("BSPrim.PositionSanityCheck", sanityOperation);
@ -453,7 +455,6 @@ public sealed class BSPrim : BSPhysObject
} }
return; return;
} }
public override OMV.Vector3 Velocity { public override OMV.Vector3 Velocity {
get { return _velocity; } get { return _velocity; }
set { set {
@ -465,6 +466,13 @@ public sealed class BSPrim : BSPhysObject
}); });
} }
} }
public override OMV.Vector3 ForceVelocity {
get { return _velocity; }
set {
_velocity = value;
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity);
}
}
public override OMV.Vector3 Torque { public override OMV.Vector3 Torque {
get { return _torque; } get { return _torque; }
set { _torque = value; set { _torque = value;
@ -490,6 +498,8 @@ public sealed class BSPrim : BSPhysObject
return _orientation; return _orientation;
} }
set { set {
if (_orientation == value)
return;
_orientation = value; _orientation = value;
// TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint?
PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
@ -534,13 +544,13 @@ public sealed class BSPrim : BSPhysObject
} }
// An object is static (does not move) if selected or not physical // An object is static (does not move) if selected or not physical
private bool IsStatic public override bool IsStatic
{ {
get { return _isSelected || !IsPhysical; } get { return _isSelected || !IsPhysical; }
} }
// An object is solid if it's not phantom and if it's not doing VolumeDetect // An object is solid if it's not phantom and if it's not doing VolumeDetect
public bool IsSolid public override bool IsSolid
{ {
get { return !IsPhantom && !_isVolumeDetect; } get { return !IsPhantom && !_isVolumeDetect; }
} }
@ -570,7 +580,7 @@ public sealed class BSPrim : BSPhysObject
// Set up the object physicalness (does gravity and collisions move this object) // Set up the object physicalness (does gravity and collisions move this object)
MakeDynamic(IsStatic); MakeDynamic(IsStatic);
// Update vehicle specific parameters // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters)
_vehicle.Refresh(); _vehicle.Refresh();
// Arrange for collision events if the simulator wants them // Arrange for collision events if the simulator wants them
@ -593,7 +603,7 @@ public sealed class BSPrim : BSPhysObject
// Recompute any linkset parameters. // Recompute any linkset parameters.
// When going from non-physical to physical, this re-enables the constraints that // When going from non-physical to physical, this re-enables the constraints that
// had been automatically disabled when the mass was set to zero. // had been automatically disabled when the mass was set to zero.
Linkset.Refresh(this); Linkset.Refresh(this, true);
DetailLog("{0},BSPrim.UpdatePhysicalParameters,exit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}", DetailLog("{0},BSPrim.UpdatePhysicalParameters,exit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}",
LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, BSBody, BSShape); LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, BSBody, BSShape);
@ -618,10 +628,18 @@ public sealed class BSPrim : BSPhysObject
BulletSimAPI.SetMassProps2(BSBody.ptr, 0f, OMV.Vector3.Zero); BulletSimAPI.SetMassProps2(BSBody.ptr, 0f, OMV.Vector3.Zero);
// There is no inertia in a static object // There is no inertia in a static object
BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr); BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr);
// Set collision detection parameters
if (PhysicsScene.Params.ccdMotionThreshold > 0f)
{
BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
}
// There can be special things needed for implementing linksets // There can be special things needed for implementing linksets
Linkset.MakeStatic(this); Linkset.MakeStatic(this);
// The activation state is 'disabled' so Bullet will not try to act on it // The activation state is 'disabled' so Bullet will not try to act on it.
BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.DISABLE_SIMULATION); BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.DISABLE_SIMULATION);
// Start it out sleeping and physical actions could wake it up.
// BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING);
BSBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter; BSBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter;
BSBody.collisionMask = CollisionFilterGroups.StaticObjectMask; BSBody.collisionMask = CollisionFilterGroups.StaticObjectMask;
@ -638,12 +656,22 @@ public sealed class BSPrim : BSPhysObject
// per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
BulletSimAPI.ClearAllForces2(BSBody.ptr); BulletSimAPI.ClearAllForces2(BSBody.ptr);
// For good measure, make sure the transform is set through to the motion state
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
// A dynamic object has mass // A dynamic object has mass
IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.ptr); IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.ptr);
OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Mass); OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Mass);
BulletSimAPI.SetMassProps2(BSBody.ptr, _mass, inertia); BulletSimAPI.SetMassProps2(BSBody.ptr, _mass, inertia);
BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr); BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr);
// Set collision detection parameters
if (PhysicsScene.Params.ccdMotionThreshold > 0f)
{
BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
}
// Various values for simulation limits // Various values for simulation limits
BulletSimAPI.SetDamping2(BSBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping); BulletSimAPI.SetDamping2(BSBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping);
BulletSimAPI.SetDeactivationTime2(BSBody.ptr, PhysicsScene.Params.deactivationTime); BulletSimAPI.SetDeactivationTime2(BSBody.ptr, PhysicsScene.Params.deactivationTime);
@ -655,8 +683,8 @@ public sealed class BSPrim : BSPhysObject
// Force activation of the object so Bullet will act on it. // Force activation of the object so Bullet will act on it.
// Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
BulletSimAPI.Activate2(BSBody.ptr, true); // BulletSimAPI.Activate2(BSBody.ptr, true);
BSBody.collisionFilter = CollisionFilterGroups.ObjectFilter; BSBody.collisionFilter = CollisionFilterGroups.ObjectFilter;
BSBody.collisionMask = CollisionFilterGroups.ObjectMask; BSBody.collisionMask = CollisionFilterGroups.ObjectMask;
@ -774,6 +802,15 @@ public sealed class BSPrim : BSPhysObject
}); });
} }
} }
public override OMV.Vector3 ForceRotationalVelocity {
get {
return _rotationalVelocity;
}
set {
_rotationalVelocity = value;
BulletSimAPI.SetAngularVelocity2(BSBody.ptr, _rotationalVelocity);
}
}
public override bool Kinematic { public override bool Kinematic {
get { return _kinematic; } get { return _kinematic; }
set { _kinematic = value; set { _kinematic = value;
@ -786,13 +823,20 @@ public sealed class BSPrim : BSPhysObject
_buoyancy = value; _buoyancy = value;
PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate() PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate()
{ {
// DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy); ForceBuoyancy = _buoyancy;
// Buoyancy is faked by changing the gravity applied to the object
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav));
}); });
} }
} }
public override float ForceBuoyancy {
get { return _buoyancy; }
set {
_buoyancy = value;
// DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
// Buoyancy is faked by changing the gravity applied to the object
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav));
}
}
// Used for MoveTo // Used for MoveTo
public override OMV.Vector3 PIDTarget { public override OMV.Vector3 PIDTarget {
@ -828,6 +872,9 @@ public sealed class BSPrim : BSPhysObject
private List<OMV.Vector3> m_accumulatedForces = new List<OMV.Vector3>(); private List<OMV.Vector3> m_accumulatedForces = new List<OMV.Vector3>();
public override void AddForce(OMV.Vector3 force, bool pushforce) { public override void AddForce(OMV.Vector3 force, bool pushforce) {
AddForce(force, pushforce, false);
}
public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
// for an object, doesn't matter if force is a pushforce or not // for an object, doesn't matter if force is a pushforce or not
if (force.IsFinite()) if (force.IsFinite())
{ {
@ -840,11 +887,12 @@ public sealed class BSPrim : BSPhysObject
m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
return; return;
} }
PhysicsScene.TaintedObject("BSPrim.AddForce", delegate() BSScene.TaintCallback addForceOperation = delegate()
{ {
OMV.Vector3 fSum = OMV.Vector3.Zero; OMV.Vector3 fSum = OMV.Vector3.Zero;
lock (m_accumulatedForces) lock (m_accumulatedForces)
{ {
// Sum the accumulated additional forces for one big force to apply once.
foreach (OMV.Vector3 v in m_accumulatedForces) foreach (OMV.Vector3 v in m_accumulatedForces)
{ {
fSum += v; fSum += v;
@ -854,7 +902,11 @@ public sealed class BSPrim : BSPhysObject
// DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum); // DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum);
// For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object. // For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object.
BulletSimAPI.ApplyCentralForce2(BSBody.ptr, fSum); BulletSimAPI.ApplyCentralForce2(BSBody.ptr, fSum);
}); };
if (inTaintTime)
addForceOperation();
else
PhysicsScene.TaintedObject("BSPrim.AddForce", addForceOperation);
} }
public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
@ -872,19 +924,19 @@ public sealed class BSPrim : BSPhysObject
float tmp; float tmp;
float returnMass = 0; float returnMass = 0;
float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f; float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f;
float hollowVolume = hollowAmount * hollowAmount; float hollowVolume = hollowAmount * hollowAmount;
switch (_pbs.ProfileShape) switch (BaseShape.ProfileShape)
{ {
case ProfileShape.Square: case ProfileShape.Square:
// default box // default box
if (_pbs.PathCurve == (byte)Extrusion.Straight) if (BaseShape.PathCurve == (byte)Extrusion.Straight)
{ {
if (hollowAmount > 0.0) if (hollowAmount > 0.0)
{ {
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Square: case HollowShape.Square:
case HollowShape.Same: case HollowShape.Same:
@ -908,19 +960,19 @@ public sealed class BSPrim : BSPhysObject
} }
} }
else if (_pbs.PathCurve == (byte)Extrusion.Curve1) else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{ {
//a tube //a tube
volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX); volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX);
tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY); tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY);
volume -= volume*tmp*tmp; volume -= volume*tmp*tmp;
if (hollowAmount > 0.0) if (hollowAmount > 0.0)
{ {
hollowVolume *= hollowAmount; hollowVolume *= hollowAmount;
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Square: case HollowShape.Square:
case HollowShape.Same: case HollowShape.Same:
@ -945,13 +997,13 @@ public sealed class BSPrim : BSPhysObject
case ProfileShape.Circle: case ProfileShape.Circle:
if (_pbs.PathCurve == (byte)Extrusion.Straight) if (BaseShape.PathCurve == (byte)Extrusion.Straight)
{ {
volume *= 0.78539816339f; // elipse base volume *= 0.78539816339f; // elipse base
if (hollowAmount > 0.0) if (hollowAmount > 0.0)
{ {
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Same: case HollowShape.Same:
case HollowShape.Circle: case HollowShape.Circle:
@ -973,10 +1025,10 @@ public sealed class BSPrim : BSPhysObject
} }
} }
else if (_pbs.PathCurve == (byte)Extrusion.Curve1) else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{ {
volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX); volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX);
tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
volume *= (1.0f - tmp * tmp); volume *= (1.0f - tmp * tmp);
if (hollowAmount > 0.0) if (hollowAmount > 0.0)
@ -985,7 +1037,7 @@ public sealed class BSPrim : BSPhysObject
// calculate the hollow volume by it's shape compared to the prim shape // calculate the hollow volume by it's shape compared to the prim shape
hollowVolume *= hollowAmount; hollowVolume *= hollowAmount;
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Same: case HollowShape.Same:
case HollowShape.Circle: case HollowShape.Circle:
@ -1009,7 +1061,7 @@ public sealed class BSPrim : BSPhysObject
break; break;
case ProfileShape.HalfCircle: case ProfileShape.HalfCircle:
if (_pbs.PathCurve == (byte)Extrusion.Curve1) if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{ {
volume *= 0.52359877559829887307710723054658f; volume *= 0.52359877559829887307710723054658f;
} }
@ -1017,7 +1069,7 @@ public sealed class BSPrim : BSPhysObject
case ProfileShape.EquilateralTriangle: case ProfileShape.EquilateralTriangle:
if (_pbs.PathCurve == (byte)Extrusion.Straight) if (BaseShape.PathCurve == (byte)Extrusion.Straight)
{ {
volume *= 0.32475953f; volume *= 0.32475953f;
@ -1025,7 +1077,7 @@ public sealed class BSPrim : BSPhysObject
{ {
// calculate the hollow volume by it's shape compared to the prim shape // calculate the hollow volume by it's shape compared to the prim shape
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Same: case HollowShape.Same:
case HollowShape.Triangle: case HollowShape.Triangle:
@ -1050,11 +1102,11 @@ public sealed class BSPrim : BSPhysObject
volume *= (1.0f - hollowVolume); volume *= (1.0f - hollowVolume);
} }
} }
else if (_pbs.PathCurve == (byte)Extrusion.Curve1) else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{ {
volume *= 0.32475953f; volume *= 0.32475953f;
volume *= 0.01f * (float)(200 - _pbs.PathScaleX); volume *= 0.01f * (float)(200 - BaseShape.PathScaleX);
tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
volume *= (1.0f - tmp * tmp); volume *= (1.0f - tmp * tmp);
if (hollowAmount > 0.0) if (hollowAmount > 0.0)
@ -1062,7 +1114,7 @@ public sealed class BSPrim : BSPhysObject
hollowVolume *= hollowAmount; hollowVolume *= hollowAmount;
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Same: case HollowShape.Same:
case HollowShape.Triangle: case HollowShape.Triangle:
@ -1102,26 +1154,26 @@ public sealed class BSPrim : BSPhysObject
float profileBegin; float profileBegin;
float profileEnd; float profileEnd;
if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible) if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible)
{ {
taperX1 = _pbs.PathScaleX * 0.01f; taperX1 = BaseShape.PathScaleX * 0.01f;
if (taperX1 > 1.0f) if (taperX1 > 1.0f)
taperX1 = 2.0f - taperX1; taperX1 = 2.0f - taperX1;
taperX = 1.0f - taperX1; taperX = 1.0f - taperX1;
taperY1 = _pbs.PathScaleY * 0.01f; taperY1 = BaseShape.PathScaleY * 0.01f;
if (taperY1 > 1.0f) if (taperY1 > 1.0f)
taperY1 = 2.0f - taperY1; taperY1 = 2.0f - taperY1;
taperY = 1.0f - taperY1; taperY = 1.0f - taperY1;
} }
else else
{ {
taperX = _pbs.PathTaperX * 0.01f; taperX = BaseShape.PathTaperX * 0.01f;
if (taperX < 0.0f) if (taperX < 0.0f)
taperX = -taperX; taperX = -taperX;
taperX1 = 1.0f - taperX; taperX1 = 1.0f - taperX;
taperY = _pbs.PathTaperY * 0.01f; taperY = BaseShape.PathTaperY * 0.01f;
if (taperY < 0.0f) if (taperY < 0.0f)
taperY = -taperY; taperY = -taperY;
taperY1 = 1.0f - taperY; taperY1 = 1.0f - taperY;
@ -1131,13 +1183,13 @@ public sealed class BSPrim : BSPhysObject
volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY);
pathBegin = (float)_pbs.PathBegin * 2.0e-5f; pathBegin = (float)BaseShape.PathBegin * 2.0e-5f;
pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f; pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f;
volume *= (pathEnd - pathBegin); volume *= (pathEnd - pathBegin);
// this is crude aproximation // this is crude aproximation
profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f; profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f;
profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f; profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
volume *= (profileEnd - profileBegin); volume *= (profileEnd - profileBegin);
returnMass = _density * volume; returnMass = _density * volume;
@ -1172,7 +1224,8 @@ public sealed class BSPrim : BSPhysObject
shape.Position = _position; shape.Position = _position;
shape.Rotation = _orientation; shape.Rotation = _orientation;
shape.Velocity = _velocity; shape.Velocity = _velocity;
shape.Scale = _scale; shape.Size = _size;
shape.Scale = Scale;
shape.Mass = _isPhysical ? _mass : 0f; shape.Mass = _isPhysical ? _mass : 0f;
shape.Buoyancy = _buoyancy; shape.Buoyancy = _buoyancy;
shape.HullKey = 0; shape.HullKey = 0;
@ -1182,7 +1235,6 @@ public sealed class BSPrim : BSPhysObject
shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse; shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse;
shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue; shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
shape.Solid = IsSolid ? ShapeData.numericFalse : ShapeData.numericTrue; shape.Solid = IsSolid ? ShapeData.numericFalse : ShapeData.numericTrue;
shape.Size = _size;
} }
// Rebuild the geometry and object. // Rebuild the geometry and object.
// This is called when the shape changes so we need to recreate the mesh/hull. // This is called when the shape changes so we need to recreate the mesh/hull.
@ -1199,11 +1251,12 @@ public sealed class BSPrim : BSPhysObject
// Create the correct physical representation for this type of object. // Create the correct physical representation for this type of object.
// Updates BSBody and BSShape with the new information. // Updates BSBody and BSShape with the new information.
// Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, _pbs, PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, BaseShape,
null, delegate(BulletBody dBody) null, delegate(BulletBody dBody)
{ {
// Called if the current prim body is about to be destroyed. // Called if the current prim body is about to be destroyed.
// Remove all the physical dependencies on the old body. // Remove all the physical dependencies on the old body.
// (Maybe someday make the changing of BSShape an event handled by BSLinkset.)
needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); needToRestoreLinkset = Linkset.RemoveBodyDependencies(this);
}); });
@ -1292,7 +1345,11 @@ public sealed class BSPrim : BSPhysObject
_acceleration = entprop.Acceleration; _acceleration = entprop.Acceleration;
_rotationalVelocity = entprop.RotationalVelocity; _rotationalVelocity = entprop.RotationalVelocity;
PositionSanityCheck2(true); // remember the current and last set values
LastEntityProperties = CurrentEntityProperties;
CurrentEntityProperties = entprop;
PositionSanityCheck(true);
DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
@ -1304,12 +1361,15 @@ public sealed class BSPrim : BSPhysObject
/* /*
else else
{ {
// For debugging, we can also report the movement of children // For debugging, report the movement of children
DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
entprop.Acceleration, entprop.RotationalVelocity); entprop.Acceleration, entprop.RotationalVelocity);
} }
*/ */
// The linkset implimentation might want to know about this.
Linkset.UpdateProperties(this);
} }
} }
} }

View File

@ -39,7 +39,6 @@ using log4net;
using OpenMetaverse; using OpenMetaverse;
// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
// Move all logic out of the C++ code and into the C# code for easier future modifications.
// Test sculpties (verified that they don't work) // Test sculpties (verified that they don't work)
// Compute physics FPS reasonably // Compute physics FPS reasonably
// Based on material, set density and friction // Based on material, set density and friction
@ -90,10 +89,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// let my minuions use my logger // let my minuions use my logger
public ILog Logger { get { return m_log; } } public ILog Logger { get { return m_log; } }
// If non-zero, the number of simulation steps between calls to the physics
// engine to output detailed physics stats. Debug logging level must be on also.
private int m_detailedStatsStep = 0;
public IMesher mesher; public IMesher mesher;
// Level of Detail values kept as float because that's what the Meshmerizer wants // Level of Detail values kept as float because that's what the Meshmerizer wants
public float MeshLOD { get; private set; } public float MeshLOD { get; private set; }
@ -112,6 +107,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
private float m_fixedTimeStep; private float m_fixedTimeStep;
private long m_simulationStep = 0; private long m_simulationStep = 0;
public long SimulationStep { get { return m_simulationStep; } } public long SimulationStep { get { return m_simulationStep; } }
private int m_taintsToProcessPerStep;
// A value of the time now so all the collision and update routines do not have to get their own // A value of the time now so all the collision and update routines do not have to get their own
// Set to 'now' just before all the prims and actors are called for collisions and updates // Set to 'now' just before all the prims and actors are called for collisions and updates
@ -131,6 +127,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
public bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects
public float PID_D { get; private set; } // derivative public float PID_D { get; private set; } // derivative
public float PID_P { get; private set; } // proportional public float PID_P { get; private set; } // proportional
@ -254,19 +251,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// The bounding box for the simulated world. The origin is 0,0,0 unless we're // The bounding box for the simulated world. The origin is 0,0,0 unless we're
// a child in a mega-region. // a child in a mega-region.
// Turns out that Bullet really doesn't care about the extents of the simulated // Bullet actually doesn't care about the extents of the simulated
// area. It tracks active objects no matter where they are. // area. It tracks active objects no matter where they are.
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
// m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
WorldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(), World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(), m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(),
m_DebugLogCallbackHandle); m_DebugLogCallbackHandle));
// Initialization to support the transition to a new API which puts most of the logic
// into the C# code so it is easier to modify and add to.
World = new BulletSim(WorldID, this, BulletSimAPI.GetSimHandle2(WorldID));
Constraints = new BSConstraintCollection(World); Constraints = new BSConstraintCollection(World);
@ -331,7 +324,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// Called directly from unmanaged code so don't do much // Called directly from unmanaged code so don't do much
private void BulletLoggerPhysLog(string msg) private void BulletLoggerPhysLog(string msg)
{ {
PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg); DetailLog("[BULLETS UNMANAGED]:" + msg);
} }
public override void Dispose() public override void Dispose()
@ -363,7 +356,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
} }
// Anything left in the unmanaged code should be cleaned out // Anything left in the unmanaged code should be cleaned out
BulletSimAPI.Shutdown(WorldID); BulletSimAPI.Shutdown2(World.ptr);
// Not logging any more // Not logging any more
PhysicsLogging.Close(); PhysicsLogging.Close();
@ -494,19 +487,19 @@ public class BSScene : PhysicsScene, IPhysicsParameters
m_simulationStep++; m_simulationStep++;
int numSubSteps = 0; int numSubSteps = 0;
// Sometimes needed for debugging to find out what happened before the step // DEBUG
// PhysicsLogging.Flush(); // DetailLog("{0},BSScene.Simulate,beforeStep,ntaimts={1},step={2}", DetailLogZero, numTaints, m_simulationStep);
try try
{ {
if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
numSubSteps = BulletSimAPI.PhysicsStep(WorldID, timeStep, m_maxSubSteps, m_fixedTimeStep, numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
DetailLog("{0},Simulate,call, nTaints={1}, simTime={2}, substeps={3}, updates={4}, colliders={5}", DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}",
DetailLogZero, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount);
} }
catch (Exception e) catch (Exception e)
{ {
@ -539,26 +532,26 @@ public class BSScene : PhysicsScene, IPhysicsParameters
} }
} }
// This is a kludge to get avatar movement updates.
// the simulator expects collisions for avatars even if there are have been no collisions. This updates
// avatar animations and stuff.
// If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
foreach (BSPhysObject bsp in m_avatars)
bsp.SendCollisions();
// The above SendCollision's batch up the collisions on the objects. // The above SendCollision's batch up the collisions on the objects.
// Now push the collisions into the simulator. // Now push the collisions into the simulator.
if (ObjectsWithCollisions.Count > 0) if (ObjectsWithCollisions.Count > 0)
{ {
foreach (BSPhysObject bsp in ObjectsWithCollisions) foreach (BSPhysObject bsp in ObjectsWithCollisions)
if (!m_avatars.Contains(bsp)) // don't call avatars twice if (!bsp.SendCollisions())
if (!bsp.SendCollisions()) {
{ // If the object is done colliding, see that it's removed from the colliding list
// If the object is done colliding, see that it's removed from the colliding list ObjectsWithNoMoreCollisions.Add(bsp);
ObjectsWithNoMoreCollisions.Add(bsp); }
}
} }
// This is a kludge to get avatar movement updates.
// The simulator expects collisions for avatars even if there are have been no collisions.
// The event updates avatar animations and stuff.
// If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
foreach (BSPhysObject bsp in m_avatars)
if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
bsp.SendCollisions();
// Objects that are done colliding are removed from the ObjectsWithCollisions list. // Objects that are done colliding are removed from the ObjectsWithCollisions list.
// Not done above because it is inside an iteration of ObjectWithCollisions. // Not done above because it is inside an iteration of ObjectWithCollisions.
if (ObjectsWithNoMoreCollisions.Count > 0) if (ObjectsWithNoMoreCollisions.Count > 0)
@ -582,19 +575,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters
} }
} }
// If enabled, call into the physics engine to dump statistics // This causes the unmanaged code to output ALL the values found in ALL the objects in the world.
if (m_detailedStatsStep > 0) // Only enable this in a limited test world with few objects.
{ // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
if ((m_simulationStep % m_detailedStatsStep) == 0)
{
BulletSimAPI.DumpBulletStatistics();
}
}
// The physics engine returns the number of milliseconds it simulated this call. // The physics engine returns the number of milliseconds it simulated this call.
// These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
// Since Bullet normally does 5 or 6 substeps, this will normally sum to about 60 FPS. // We multiply by 55 to give a recognizable running rate (55 or less).
return numSubSteps * m_fixedTimeStep * 1000; return numSubSteps * m_fixedTimeStep * 1000 * 55;
// return timeStep * 1000 * 55;
} }
// Something has collided // Something has collided
@ -617,7 +606,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
BSPhysObject collidee = null; BSPhysObject collidee = null;
PhysObjects.TryGetValue(collidingWith, out collidee); PhysObjects.TryGetValue(collidingWith, out collidee);
DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
{ {
@ -703,6 +692,35 @@ public class BSScene : PhysicsScene, IPhysicsParameters
{ {
if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process
{ {
// swizzle a new list into the list location so we can process what's there
int taintCount = m_taintsToProcessPerStep;
TaintCallbackEntry oneCallback = new TaintCallbackEntry();
while (_taintedObjects.Count > 0 && taintCount-- > 0)
{
bool gotOne = false;
lock (_taintLock)
{
if (_taintedObjects.Count > 0)
{
oneCallback = _taintedObjects[0];
_taintedObjects.RemoveAt(0);
gotOne = true;
}
}
if (gotOne)
{
try
{
DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG
oneCallback.callback();
}
catch (Exception e)
{
m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e);
}
}
}
/*
// swizzle a new list into the list location so we can process what's there // swizzle a new list into the list location so we can process what's there
List<TaintCallbackEntry> oldList; List<TaintCallbackEntry> oldList;
lock (_taintLock) lock (_taintLock)
@ -715,6 +733,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
{ {
try try
{ {
DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
tcbe.callback(); tcbe.callback();
} }
catch (Exception e) catch (Exception e)
@ -723,6 +742,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
} }
} }
oldList.Clear(); oldList.Clear();
*/
} }
} }
@ -780,6 +800,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
delegate float ParamGet(BSScene scene); delegate float ParamGet(BSScene scene);
delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
private struct ParameterDefn private struct ParameterDefn
{ {
@ -789,6 +810,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public ParamUser userParam; // get the value from the configuration file public ParamUser userParam; // get the value from the configuration file
public ParamGet getter; // return the current value stored for this parameter public ParamGet getter; // return the current value stored for this parameter
public ParamSet setter; // set the current value for this parameter public ParamSet setter; // set the current value for this parameter
public SetOnObject onObject; // set the value on an object in the physical domain
public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
{ {
name = n; name = n;
@ -797,6 +819,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters
userParam = u; userParam = u;
getter = g; getter = g;
setter = s; setter = s;
onObject = null;
}
public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
{
name = n;
desc = d;
defaultValue = v;
userParam = u;
getter = g;
setter = s;
onObject = o;
} }
} }
@ -818,6 +851,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// //
// The single letter parameters for the delegates are: // The single letter parameters for the delegates are:
// s = BSScene // s = BSScene
// o = BSPhysObject
// p = string parameter name // p = string parameter name
// l = localID of referenced object // l = localID of referenced object
// v = float value // v = float value
@ -834,6 +868,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
(s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); }, (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); },
(s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ), (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ),
new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
ConfigurationParameters.numericTrue,
(s,cf,p,v) => { s.ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, s.BoolNumeric(v)); },
(s) => { return s.NumericBool(s.ShouldUseHullsForPhysicalObjects); },
(s,p,l,v) => { s.ShouldUseHullsForPhysicalObjects = s.BoolNumeric(v); } ),
new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
8f, 8f,
@ -876,6 +915,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
(s) => { return (float)s.m_maxUpdatesPerFrame; }, (s) => { return (float)s.m_maxUpdatesPerFrame; },
(s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
100f,
(s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
(s) => { return (float)s.m_taintsToProcessPerStep; },
(s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
10000.01f, 10000.01f,
(s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); },
@ -917,70 +961,84 @@ public class BSScene : PhysicsScene, IPhysicsParameters
-9.80665f, -9.80665f,
(s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].gravity; }, (s) => { return s.m_params[0].gravity; },
(s,p,l,v) => { s.m_params[0].gravity = v; s.TaintedUpdateParameter(p,l,v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); },
(s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
0f, 0f,
(s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linearDamping; }, (s) => { return s.m_params[0].linearDamping; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); },
(s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ),
new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
0f, 0f,
(s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].angularDamping; }, (s) => { return s.m_params[0].angularDamping; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); },
(s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ),
new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
0.2f, 0.2f,
(s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].deactivationTime; }, (s) => { return s.m_params[0].deactivationTime; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); },
(s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.BSBody.ptr, v); } ),
new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
0.8f, 0.8f,
(s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linearSleepingThreshold; }, (s) => { return s.m_params[0].linearSleepingThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ),
new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
1.0f, 1.0f,
(s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].angularSleepingThreshold; }, (s) => { return s.m_params[0].angularSleepingThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, 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 0f, // set to zero to disable
(s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].ccdMotionThreshold; }, (s) => { return s.m_params[0].ccdMotionThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.BSBody.ptr, v); } ),
new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
0f, 0f,
(s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].ccdSweptSphereRadius; }, (s) => { return s.m_params[0].ccdSweptSphereRadius; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); },
(s,o,v) => { BulletSimAPI.SetCcdSweepSphereRadius2(o.BSBody.ptr, v); } ),
new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
0.1f, 0.1f,
(s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].contactProcessingThreshold; }, (s) => { return s.m_params[0].contactProcessingThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.BSBody.ptr, v); } ),
new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
0.5f, 0.5f,
(s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].terrainFriction; }, (s) => { return s.m_params[0].terrainFriction; },
(s,p,l,v) => { s.m_params[0].terrainFriction = v; s.TaintedUpdateParameter(p,l,v); } ), (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ),
new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
0.8f, 0.8f,
(s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].terrainHitFraction; }, (s) => { return s.m_params[0].terrainHitFraction; },
(s,p,l,v) => { s.m_params[0].terrainHitFraction = v; s.TaintedUpdateParameter(p,l,v); } ), (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ),
new ParameterDefn("TerrainRestitution", "Bouncyness" , new ParameterDefn("TerrainRestitution", "Bouncyness" ,
0f, 0f,
(s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].terrainRestitution; }, (s) => { return s.m_params[0].terrainRestitution; },
(s,p,l,v) => { s.m_params[0].terrainRestitution = v; s.TaintedUpdateParameter(p,l,v); } ), (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ),
new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
0.2f, 0.2f,
(s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarFriction; }, (s) => { return s.m_params[0].avatarFriction; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ),
new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
10f,
(s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarStandingFriction; },
(s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
60f, 60f,
(s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); },
@ -1070,12 +1128,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
(s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
0.1f, 0.001f,
(s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linkConstraintCFM; }, (s) => { return s.m_params[0].linkConstraintCFM; },
(s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ),
new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
0.2f, 0.8f,
(s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linkConstraintERP; }, (s) => { return s.m_params[0].linkConstraintERP; },
(s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
@ -1085,11 +1143,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(s) => { return s.m_params[0].linkConstraintSolverIterations; }, (s) => { return s.m_params[0].linkConstraintSolverIterations; },
(s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ), (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ),
new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)", new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)",
0f, 0f,
(s,cf,p,v) => { s.m_detailedStatsStep = cf.GetInt(p, (int)v); }, (s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
(s) => { return (float)s.m_detailedStatsStep; }, (s) => { return (float)s.m_params[0].physicsLoggingFrames; },
(s,p,l,v) => { s.m_detailedStatsStep = (int)v; } ), (s,p,l,v) => { s.m_params[0].physicsLoggingFrames = (int)v; } ),
}; };
// Convert a boolean to our numeric true and false values // Convert a boolean to our numeric true and false values
@ -1197,52 +1255,54 @@ public class BSScene : PhysicsScene, IPhysicsParameters
return ret; return ret;
} }
// check to see if we are updating a parameter for a particular or all of the prims
protected void UpdateParameterObject(ref float loc, string parm, uint localID, float val)
{
List<uint> operateOn;
lock (PhysObjects) operateOn = new List<uint>(PhysObjects.Keys);
UpdateParameterSet(operateOn, ref loc, parm, localID, val);
}
// update all the localIDs specified // update all the localIDs specified
// If the local ID is APPLY_TO_NONE, just change the default value // If the local ID is APPLY_TO_NONE, just change the default value
// If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
// If the localID is a specific object, apply the parameter change to only that object // If the localID is a specific object, apply the parameter change to only that object
protected void UpdateParameterSet(List<uint> lIDs, ref float defaultLoc, string parm, uint localID, float val) protected void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val)
{ {
List<uint> objectIDs = new List<uint>();
switch (localID) switch (localID)
{ {
case PhysParameterEntry.APPLY_TO_NONE: case PhysParameterEntry.APPLY_TO_NONE:
defaultLoc = val; // setting only the default value defaultLoc = val; // setting only the default value
// This will cause a call into the physical world if some operation is specified (SetOnObject).
objectIDs.Add(TERRAIN_ID);
TaintedUpdateParameter(parm, objectIDs, val);
break; break;
case PhysParameterEntry.APPLY_TO_ALL: case PhysParameterEntry.APPLY_TO_ALL:
defaultLoc = val; // setting ALL also sets the default value defaultLoc = val; // setting ALL also sets the default value
List<uint> objectIDs = lIDs; lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
string xparm = parm.ToLower(); TaintedUpdateParameter(parm, objectIDs, val);
float xval = val;
TaintedObject("BSScene.UpdateParameterSet", delegate() {
foreach (uint lID in objectIDs)
{
BulletSimAPI.UpdateParameter(WorldID, lID, xparm, xval);
}
});
break; break;
default: default:
// setting only one localID // setting only one localID
TaintedUpdateParameter(parm, localID, val); objectIDs.Add(localID);
TaintedUpdateParameter(parm, objectIDs, val);
break; break;
} }
} }
// schedule the actual updating of the paramter to when the phys engine is not busy // schedule the actual updating of the paramter to when the phys engine is not busy
protected void TaintedUpdateParameter(string parm, uint localID, float val) protected void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
{ {
uint xlocalID = localID;
string xparm = parm.ToLower();
float xval = val; float xval = val;
TaintedObject("BSScene.TaintedUpdateParameter", delegate() { List<uint> xlIDs = lIDs;
BulletSimAPI.UpdateParameter(WorldID, xlocalID, xparm, xval); string xparm = parm;
TaintedObject("BSScene.UpdateParameterSet", delegate() {
ParameterDefn thisParam;
if (TryGetParameter(xparm, out thisParam))
{
if (thisParam.onObject != null)
{
foreach (uint lID in xlIDs)
{
BSPhysObject theObject = null;
PhysObjects.TryGetValue(lID, out theObject);
thisParam.onObject(this, theObject, xval);
}
}
}
}); });
} }
@ -1270,6 +1330,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public void DetailLog(string msg, params Object[] args) public void DetailLog(string msg, params Object[] args)
{ {
PhysicsLogging.Write(msg, args); PhysicsLogging.Write(msg, args);
// Add the Flush() if debugging crashes to get all the messages written out.
// PhysicsLogging.Flush();
} }
// used to fill in the LocalID when there isn't one // used to fill in the LocalID when there isn't one
public const string DetailLogZero = "0000000000"; public const string DetailLogZero = "0000000000";

View File

@ -51,7 +51,7 @@ public class BSShapeCollection : IDisposable
} }
// Description of a hull. // Description of a hull.
// Meshes and hulls have the same shape hash key but we only need hulls for efficient physical objects // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations.
private struct HullDesc private struct HullDesc
{ {
public IntPtr ptr; public IntPtr ptr;
@ -59,17 +59,9 @@ public class BSShapeCollection : IDisposable
public DateTime lastReferenced; public DateTime lastReferenced;
} }
private struct BodyDesc // The sharable set of meshes and hulls. Indexed by their shape hash.
{ private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
public IntPtr ptr; private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
// Bodies are only used once so reference count is always either one or zero
public int referenceCount;
public DateTime lastReferenced;
}
private Dictionary<ulong, MeshDesc> Meshes = new Dictionary<ulong, MeshDesc>();
private Dictionary<ulong, HullDesc> Hulls = new Dictionary<ulong, HullDesc>();
private Dictionary<uint, BodyDesc> Bodies = new Dictionary<uint, BodyDesc>();
public BSShapeCollection(BSScene physScene) public BSShapeCollection(BSScene physScene)
{ {
@ -92,8 +84,12 @@ public class BSShapeCollection : IDisposable
// First checks the shape and updates that if necessary then makes // First checks the shape and updates that if necessary then makes
// sure the body is of the right type. // sure the body is of the right type.
// Return 'true' if either the body or the shape changed. // Return 'true' if either the body or the shape changed.
// 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
// the current shape or body is destroyed. This allows the caller to remove any
// higher level dependencies on the shape or body. Mostly used for LinkSets to
// remove the physical constraints before the body is destroyed.
// Called at taint-time!! // Called at taint-time!!
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim, public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
ShapeData shapeData, PrimitiveBaseShape pbs, ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
{ {
@ -103,7 +99,8 @@ public class BSShapeCollection : IDisposable
lock (m_collectionActivityLock) lock (m_collectionActivityLock)
{ {
// Do we have the correct geometry for this type of object? // Do we have the correct geometry for this type of object?
// Updates prim.BSShape with information/pointers to requested shape // Updates prim.BSShape with information/pointers to shape.
// CreateGeom returns 'true' of BSShape as changed to a new shape.
bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback); bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback);
// If we had to select a new shape geometry for the object, // If we had to select a new shape geometry for the object,
// rebuild the body around it. // rebuild the body around it.
@ -120,26 +117,24 @@ public class BSShapeCollection : IDisposable
// Track another user of a body // Track another user of a body
// We presume the caller has allocated the body. // We presume the caller has allocated the body.
// Bodies only have one user so the reference count is either 1 or 0. // Bodies only have one user so the body is just put into the world if not already there.
public void ReferenceBody(BulletBody body, bool atTaintTime) public void ReferenceBody(BulletBody body, bool inTaintTime)
{ {
lock (m_collectionActivityLock) lock (m_collectionActivityLock)
{ {
BodyDesc bodyDesc; DetailLog("{0},BSShapeCollection.ReferenceBody,newBody", body.ID, body);
if (Bodies.TryGetValue(body.ID, out bodyDesc)) BSScene.TaintCallback createOperation = delegate()
{ {
bodyDesc.referenceCount++; if (!BulletSimAPI.IsInWorld2(body.ptr))
DetailLog("{0},BSShapeCollection.ReferenceBody,existingBody,body={1},ref={2}", body.ID, body, bodyDesc.referenceCount); {
} BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
}
};
if (inTaintTime)
createOperation();
else else
{ PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation);
// New entry
bodyDesc.ptr = body.ptr;
bodyDesc.referenceCount = 1;
DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,ref={1}", body.ID, body, bodyDesc.referenceCount);
}
bodyDesc.lastReferenced = System.DateTime.Now;
Bodies[body.ID] = bodyDesc;
} }
} }
@ -152,42 +147,25 @@ public class BSShapeCollection : IDisposable
lock (m_collectionActivityLock) lock (m_collectionActivityLock)
{ {
BodyDesc bodyDesc; BSScene.TaintCallback removeOperation = delegate()
if (Bodies.TryGetValue(body.ID, out bodyDesc))
{ {
bodyDesc.referenceCount--; DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}",
bodyDesc.lastReferenced = System.DateTime.Now; body.ID, body.ptr.ToString("X"), inTaintTime);
Bodies[body.ID] = bodyDesc; // If the caller needs to know the old body is going away, pass the event up.
DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount); if (bodyCallback != null) bodyCallback(body);
// If body is no longer being used, free it -- bodies are never shared. // It may have already been removed from the world in which case the next is a NOOP.
if (bodyDesc.referenceCount == 0) BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
{
Bodies.Remove(body.ID);
BSScene.TaintCallback removeOperation = delegate()
{
DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}",
body.ID, body.ptr.ToString("X"));
// If the caller needs to know the old body is going away, pass the event up.
if (bodyCallback != null) bodyCallback(body);
// Zero any reference to the shape so it is not freed when the body is deleted. // Zero any reference to the shape so it is not freed when the body is deleted.
BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero); BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
// It may have already been removed from the world in which case the next is a NOOP. BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr); };
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr); // If already in taint-time, do the operations now. Otherwise queue for later.
}; if (inTaintTime)
// If already in taint-time, do the operations now. Otherwise queue for later. removeOperation();
if (inTaintTime)
removeOperation();
else
PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
}
}
else else
{ PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount);
}
} }
} }
@ -208,7 +186,7 @@ public class BSShapeCollection : IDisposable
{ {
// There is an existing instance of this mesh. // There is an existing instance of this mesh.
meshDesc.referenceCount++; meshDesc.referenceCount++;
DetailLog("{0},BSShapeColliction.ReferenceShape,existingMesh,key={1},cnt={2}", DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
} }
else else
@ -217,7 +195,7 @@ public class BSShapeCollection : IDisposable
meshDesc.ptr = shape.ptr; meshDesc.ptr = shape.ptr;
// We keep a reference to the underlying IMesh data so a hull can be built // We keep a reference to the underlying IMesh data so a hull can be built
meshDesc.referenceCount = 1; meshDesc.referenceCount = 1;
DetailLog("{0},BSShapeColliction.ReferenceShape,newMesh,key={1},cnt={2}", DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
ret = true; ret = true;
} }
@ -230,7 +208,7 @@ public class BSShapeCollection : IDisposable
{ {
// There is an existing instance of this hull. // There is an existing instance of this hull.
hullDesc.referenceCount++; hullDesc.referenceCount++;
DetailLog("{0},BSShapeColliction.ReferenceShape,existingHull,key={1},cnt={2}", DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
} }
else else
@ -238,7 +216,7 @@ public class BSShapeCollection : IDisposable
// This is a new reference to a hull // This is a new reference to a hull
hullDesc.ptr = shape.ptr; hullDesc.ptr = shape.ptr;
hullDesc.referenceCount = 1; hullDesc.referenceCount = 1;
DetailLog("{0},BSShapeColliction.ReferenceShape,newHull,key={1},cnt={2}", DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
ret = true; ret = true;
@ -256,37 +234,42 @@ public class BSShapeCollection : IDisposable
} }
// Release the usage of a shape. // Release the usage of a shape.
// The collisionObject is released since it is a copy of the real collision shape. public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
public void DereferenceShape(BulletShape shape, bool atTaintTime, ShapeDestructionCallback shapeCallback)
{ {
if (shape.ptr == IntPtr.Zero) if (shape.ptr == IntPtr.Zero)
return; return;
BSScene.TaintCallback dereferenceOperation = delegate() BSScene.TaintCallback dereferenceOperation = delegate()
{ {
switch (shape.type) if (shape.ptr != IntPtr.Zero)
{ {
case ShapeData.PhysicsShapeType.SHAPE_HULL: if (shape.isNativeShape)
DereferenceHull(shape, shapeCallback); {
break;
case ShapeData.PhysicsShapeType.SHAPE_MESH:
DereferenceMesh(shape, shapeCallback);
break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
break;
default:
// Native shapes are not tracked and are released immediately // Native shapes are not tracked and are released immediately
if (shape.ptr != IntPtr.Zero & shape.isNativeShape) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime);
if (shapeCallback != null) shapeCallback(shape);
BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
}
else
{
switch (shape.type)
{ {
DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", case ShapeData.PhysicsShapeType.SHAPE_HULL:
BSScene.DetailLogZero, shape.ptr.ToString("X"), atTaintTime); DereferenceHull(shape, shapeCallback);
if (shapeCallback != null) shapeCallback(shape); break;
BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); case ShapeData.PhysicsShapeType.SHAPE_MESH:
DereferenceMesh(shape, shapeCallback);
break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
break;
default:
break;
} }
break; }
} }
}; };
if (atTaintTime) if (inTaintTime)
{ {
lock (m_collectionActivityLock) lock (m_collectionActivityLock)
{ {
@ -336,19 +319,31 @@ public class BSShapeCollection : IDisposable
// Create the geometry information in Bullet for later use. // Create the geometry information in Bullet for later use.
// The objects needs a hull if it's physical otherwise a mesh is enough. // The objects needs a hull if it's physical otherwise a mesh is enough.
// No locking here because this is done when we know physics is not simulating. // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
// if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used. // shared geometries will be used. If the parameters of the existing shape are the same
// as this request, the shape is not rebuilt.
// Info in prim.BSShape is updated to the new shape.
// Returns 'true' if the geometry was rebuilt. // Returns 'true' if the geometry was rebuilt.
// Called at taint-time! // Called at taint-time!
private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData, private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeData shapeData,
PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback)
{ {
bool ret = false; bool ret = false;
bool haveShape = false; bool haveShape = false;
bool nativeShapePossible = true; bool nativeShapePossible = true;
if (shapeData.Type == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
{
// an avatar capsule is close to a native shape (it is not shared)
ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_AVATAR,
ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.BSShape);
haveShape = true;
}
// If the prim attributes are simple, this could be a simple Bullet native shape // If the prim attributes are simple, this could be a simple Bullet native shape
if (nativeShapePossible if (!haveShape
&& pbs != null
&& nativeShapePossible
&& ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim) && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim)
|| (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
&& pbs.ProfileHollow == 0 && pbs.ProfileHollow == 0
@ -358,7 +353,8 @@ public class BSShapeCollection : IDisposable
&& pbs.PathScaleX == 100 && pbs.PathScaleY == 100 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
&& pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
{ {
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)
{ {
haveShape = true; haveShape = true;
if (forceRebuild if (forceRebuild
@ -372,7 +368,7 @@ public class BSShapeCollection : IDisposable
prim.LocalID, forceRebuild, prim.BSShape); prim.LocalID, forceRebuild, prim.BSShape);
} }
} }
else if (pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
{ {
haveShape = true; haveShape = true;
if (forceRebuild if (forceRebuild
@ -390,9 +386,9 @@ public class BSShapeCollection : IDisposable
// If a simple shape is not happening, create a mesh and possibly a hull. // If a simple shape is not happening, create a mesh and possibly a hull.
// Note that if it's a native shape, the check for physical/non-physical is not // Note that if it's a native shape, the check for physical/non-physical is not
// made. Native shapes are best used in either case. // made. Native shapes are best used in either case.
if (!haveShape) if (!haveShape && pbs != null)
{ {
if (prim.IsPhysical) if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects)
{ {
// Update prim.BSShape to reference a hull of this shape. // Update prim.BSShape to reference a hull of this shape.
ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback); ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback);
@ -409,12 +405,12 @@ public class BSShapeCollection : IDisposable
return ret; return ret;
} }
// Creates a native shape and assignes it to prim.BSShape // Creates a native shape and assignes it to prim.BSShape.
private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData, // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
private bool GetReferenceToNativeShape(BSPhysObject prim, ShapeData shapeData,
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey,
ShapeDestructionCallback shapeCallback) ShapeDestructionCallback shapeCallback)
{ {
BulletShape newShape;
shapeData.Type = shapeType; shapeData.Type = shapeType;
// Bullet native objects are scaled by the Bullet engine so pass the size in // Bullet native objects are scaled by the Bullet engine so pass the size in
@ -424,29 +420,48 @@ public class BSShapeCollection : IDisposable
// release any previous shape // release any previous shape
DereferenceShape(prim.BSShape, true, shapeCallback); DereferenceShape(prim.BSShape, true, shapeCallback);
// Native shapes are always built independently. BulletShape newShape = BuildPhysicalNativeShape(shapeType, shapeData, shapeKey);
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
newShape.shapeKey = (ulong)shapeKey;
newShape.isNativeShape = true;
// Don't need to do a 'ReferenceShape()' here because native shapes are not tracked. // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
// DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1}", shapeData.ID, newShape); DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
shapeData.ID, newShape, shapeData.Scale);
prim.BSShape = newShape; prim.BSShape = newShape;
return true; return true;
} }
private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType,
ShapeData shapeData, ShapeData.FixedShapeKey shapeKey)
{
BulletShape newShape;
if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
{
newShape = new BulletShape(
BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1.0f, 1.0f, shapeData.Scale),
shapeType);
}
else
{
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
}
newShape.shapeKey = (System.UInt64)shapeKey;
newShape.isNativeShape = true;
return newShape;
}
// Builds a mesh shape in the physical world and updates prim.BSShape. // Builds a mesh shape in the physical world and updates prim.BSShape.
// Dereferences previous shape in BSShape and adds a reference for this new shape. // Dereferences previous shape in BSShape and adds a reference for this new shape.
// Returns 'true' of a mesh was actually built. Otherwise . // Returns 'true' of a mesh was actually built. Otherwise .
// Called at taint-time! // Called at taint-time!
private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs, private bool GetReferenceToMesh(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback) ShapeDestructionCallback shapeCallback)
{ {
BulletShape newShape = new BulletShape(IntPtr.Zero); BulletShape newShape = new BulletShape(IntPtr.Zero);
float lod; float lod;
ulong newMeshKey = ComputeShapeKey(shapeData, pbs, out lod); System.UInt64 newMeshKey = ComputeShapeKey(shapeData, pbs, out lod);
// if this new shape is the same as last time, don't recreate the mesh // if this new shape is the same as last time, don't recreate the mesh
if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH) if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH)
@ -459,6 +474,8 @@ public class BSShapeCollection : IDisposable
DereferenceShape(prim.BSShape, true, shapeCallback); DereferenceShape(prim.BSShape, true, shapeCallback);
newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod); newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod);
// Take evasive action if the mesh was not constructed.
newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
ReferenceShape(newShape); ReferenceShape(newShape);
@ -469,10 +486,10 @@ public class BSShapeCollection : IDisposable
return true; // 'true' means a new shape has been added to this prim return true; // 'true' means a new shape has been added to this prim
} }
private BulletShape CreatePhysicalMesh(string objName, ulong newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
{ {
IMesh meshData = null; IMesh meshData = null;
IntPtr meshPtr; IntPtr meshPtr = IntPtr.Zero;
MeshDesc meshDesc; MeshDesc meshDesc;
if (Meshes.TryGetValue(newMeshKey, out meshDesc)) if (Meshes.TryGetValue(newMeshKey, out meshDesc))
{ {
@ -484,23 +501,26 @@ public class BSShapeCollection : IDisposable
// Pass false for physicalness as this creates some sort of bounding box which we don't need // Pass false for physicalness as this creates some sort of bounding box which we don't need
meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
int[] indices = meshData.getIndexListAsInt(); if (meshData != null)
List<OMV.Vector3> vertices = meshData.getVertexList();
float[] verticesAsFloats = new float[vertices.Count * 3];
int vi = 0;
foreach (OMV.Vector3 vv in vertices)
{ {
verticesAsFloats[vi++] = vv.X; int[] indices = meshData.getIndexListAsInt();
verticesAsFloats[vi++] = vv.Y; List<OMV.Vector3> vertices = meshData.getVertexList();
verticesAsFloats[vi++] = vv.Z;
float[] verticesAsFloats = new float[vertices.Count * 3];
int vi = 0;
foreach (OMV.Vector3 vv in vertices)
{
verticesAsFloats[vi++] = vv.X;
verticesAsFloats[vi++] = vv.Y;
verticesAsFloats[vi++] = vv.Z;
}
// m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
// LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
} }
// m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
// LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
} }
BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH);
newShape.shapeKey = newMeshKey; newShape.shapeKey = newMeshKey;
@ -510,13 +530,13 @@ public class BSShapeCollection : IDisposable
// See that hull shape exists in the physical world and update prim.BSShape. // See that hull shape exists in the physical world and update prim.BSShape.
// We could be creating the hull because scale changed or whatever. // We could be creating the hull because scale changed or whatever.
private bool GetReferenceToHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs, private bool GetReferenceToHull(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback) ShapeDestructionCallback shapeCallback)
{ {
BulletShape newShape; BulletShape newShape;
float lod; float lod;
ulong newHullKey = ComputeShapeKey(shapeData, pbs, out lod); System.UInt64 newHullKey = ComputeShapeKey(shapeData, pbs, out lod);
// if the hull hasn't changed, don't rebuild it // if the hull hasn't changed, don't rebuild it
if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL)
@ -525,10 +545,11 @@ public class BSShapeCollection : IDisposable
DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}", DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}",
prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X")); prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
// Remove usage of the previous shape. Also removes reference to underlying mesh if it is a hull. // Remove usage of the previous shape.
DereferenceShape(prim.BSShape, true, shapeCallback); DereferenceShape(prim.BSShape, true, shapeCallback);
newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod); newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod);
newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
ReferenceShape(newShape); ReferenceShape(newShape);
@ -539,10 +560,10 @@ public class BSShapeCollection : IDisposable
} }
List<ConvexResult> m_hulls; List<ConvexResult> m_hulls;
private BulletShape CreatePhysicalHull(string objName, ulong newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
{ {
IntPtr hullPtr; IntPtr hullPtr = IntPtr.Zero;
HullDesc hullDesc; HullDesc hullDesc;
if (Hulls.TryGetValue(newHullKey, out hullDesc)) if (Hulls.TryGetValue(newHullKey, out hullDesc))
{ {
@ -554,86 +575,89 @@ public class BSShapeCollection : IDisposable
// Build a new hull in the physical world // Build a new hull in the physical world
// Pass false for physicalness as this creates some sort of bounding box which we don't need // Pass false for physicalness as this creates some sort of bounding box which we don't need
IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
if (meshData != null)
int[] indices = meshData.getIndexListAsInt();
List<OMV.Vector3> vertices = meshData.getVertexList();
//format conversion from IMesh format to DecompDesc format
List<int> convIndices = new List<int>();
List<float3> convVertices = new List<float3>();
for (int ii = 0; ii < indices.GetLength(0); ii++)
{ {
convIndices.Add(indices[ii]);
}
foreach (OMV.Vector3 vv in vertices)
{
convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
}
// setup and do convex hull conversion int[] indices = meshData.getIndexListAsInt();
m_hulls = new List<ConvexResult>(); List<OMV.Vector3> vertices = meshData.getVertexList();
DecompDesc dcomp = new DecompDesc();
dcomp.mIndices = convIndices;
dcomp.mVertices = convVertices;
ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
// create the hull into the _hulls variable
convexBuilder.process(dcomp);
// Convert the vertices and indices for passing to unmanaged. //format conversion from IMesh format to DecompDesc format
// The hull information is passed as a large floating point array. List<int> convIndices = new List<int>();
// The format is: List<float3> convVertices = new List<float3>();
// convHulls[0] = number of hulls for (int ii = 0; ii < indices.GetLength(0); ii++)
// convHulls[1] = number of vertices in first hull
// convHulls[2] = hull centroid X coordinate
// convHulls[3] = hull centroid Y coordinate
// convHulls[4] = hull centroid Z coordinate
// convHulls[5] = first hull vertex X
// convHulls[6] = first hull vertex Y
// convHulls[7] = first hull vertex Z
// convHulls[8] = second hull vertex X
// ...
// convHulls[n] = number of vertices in second hull
// convHulls[n+1] = second hull centroid X coordinate
// ...
//
// TODO: is is very inefficient. Someday change the convex hull generator to return
// data structures that do not need to be converted in order to pass to Bullet.
// And maybe put the values directly into pinned memory rather than marshaling.
int hullCount = m_hulls.Count;
int totalVertices = 1; // include one for the count of the hulls
foreach (ConvexResult cr in m_hulls)
{
totalVertices += 4; // add four for the vertex count and centroid
totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
}
float[] convHulls = new float[totalVertices];
convHulls[0] = (float)hullCount;
int jj = 1;
foreach (ConvexResult cr in m_hulls)
{
// copy vertices for index access
float3[] verts = new float3[cr.HullVertices.Count];
int kk = 0;
foreach (float3 ff in cr.HullVertices)
{ {
verts[kk++] = ff; convIndices.Add(indices[ii]);
}
foreach (OMV.Vector3 vv in vertices)
{
convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
} }
// add to the array one hull's worth of data // setup and do convex hull conversion
convHulls[jj++] = cr.HullIndices.Count; m_hulls = new List<ConvexResult>();
convHulls[jj++] = 0f; // centroid x,y,z DecompDesc dcomp = new DecompDesc();
convHulls[jj++] = 0f; dcomp.mIndices = convIndices;
convHulls[jj++] = 0f; dcomp.mVertices = convVertices;
foreach (int ind in cr.HullIndices) ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
// create the hull into the _hulls variable
convexBuilder.process(dcomp);
// Convert the vertices and indices for passing to unmanaged.
// The hull information is passed as a large floating point array.
// The format is:
// convHulls[0] = number of hulls
// convHulls[1] = number of vertices in first hull
// convHulls[2] = hull centroid X coordinate
// convHulls[3] = hull centroid Y coordinate
// convHulls[4] = hull centroid Z coordinate
// convHulls[5] = first hull vertex X
// convHulls[6] = first hull vertex Y
// convHulls[7] = first hull vertex Z
// convHulls[8] = second hull vertex X
// ...
// convHulls[n] = number of vertices in second hull
// convHulls[n+1] = second hull centroid X coordinate
// ...
//
// TODO: is is very inefficient. Someday change the convex hull generator to return
// data structures that do not need to be converted in order to pass to Bullet.
// And maybe put the values directly into pinned memory rather than marshaling.
int hullCount = m_hulls.Count;
int totalVertices = 1; // include one for the count of the hulls
foreach (ConvexResult cr in m_hulls)
{ {
convHulls[jj++] = verts[ind].x; totalVertices += 4; // add four for the vertex count and centroid
convHulls[jj++] = verts[ind].y; totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
convHulls[jj++] = verts[ind].z;
} }
float[] convHulls = new float[totalVertices];
convHulls[0] = (float)hullCount;
int jj = 1;
foreach (ConvexResult cr in m_hulls)
{
// copy vertices for index access
float3[] verts = new float3[cr.HullVertices.Count];
int kk = 0;
foreach (float3 ff in cr.HullVertices)
{
verts[kk++] = ff;
}
// add to the array one hull's worth of data
convHulls[jj++] = cr.HullIndices.Count;
convHulls[jj++] = 0f; // centroid x,y,z
convHulls[jj++] = 0f;
convHulls[jj++] = 0f;
foreach (int ind in cr.HullIndices)
{
convHulls[jj++] = verts[ind].x;
convHulls[jj++] = verts[ind].y;
convHulls[jj++] = verts[ind].z;
}
}
// create the hull data structure in Bullet
hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
} }
// create the hull data structure in Bullet
hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
} }
BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL);
@ -652,32 +676,77 @@ public class BSShapeCollection : IDisposable
// Create a hash of all the shape parameters to be used as a key // Create a hash of all the shape parameters to be used as a key
// for this particular shape. // for this particular shape.
private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod) private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod)
{ {
// level of detail based on size and type of the object // level of detail based on size and type of the object
float lod = PhysicsScene.MeshLOD; float lod = PhysicsScene.MeshLOD;
if (pbs.SculptEntry) if (pbs.SculptEntry)
lod = PhysicsScene.SculptLOD; lod = PhysicsScene.SculptLOD;
// Mega prims usually get more detail because one can interact with shape approximations at this size.
float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z)); float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z));
if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) if (maxAxis > PhysicsScene.MeshMegaPrimThreshold)
lod = PhysicsScene.MeshMegaPrimLOD; lod = PhysicsScene.MeshMegaPrimLOD;
retLod = lod; retLod = lod;
return (ulong)pbs.GetMeshKey(shapeData.Size, lod); return pbs.GetMeshKey(shapeData.Size, lod);
} }
// For those who don't want the LOD // For those who don't want the LOD
private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs) private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs)
{ {
float lod; float lod;
return ComputeShapeKey(shapeData, pbs, out lod); return ComputeShapeKey(shapeData, pbs, out lod);
} }
private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs)
{
// If the shape was successfully created, nothing more to do
if (newShape.ptr != IntPtr.Zero)
return newShape;
// The most common reason for failure is that an underlying asset is not available
// If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
if (pbs.SculptEntry && !prim.LastAssetBuildFailed && pbs.SculptTexture != OMV.UUID.Zero)
{
prim.LastAssetBuildFailed = true;
BSPhysObject xprim = prim;
Util.FireAndForget(delegate
{
RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
if (assetProvider != null)
{
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;
yprim.BaseShape.SculptData = new byte[asset.Data.Length];
asset.Data.CopyTo(yprim.BaseShape.SculptData, 0);
// This will cause the prim to see that the filler shape is not the right
// one and try again to build the object.
yprim.ForceBodyShapeRebuild(false);
});
}
});
}
// While we figure out the real problem, stick a simple native shape on the object.
BulletShape fillinShape =
BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_SPHERE, shapeData, ShapeData.FixedShapeKey.KEY_SPHERE);
return fillinShape;
}
// Create a body object in Bullet. // Create a body object in Bullet.
// Updates prim.BSBody with the information about the new body if one is created. // Updates prim.BSBody with the information about the new body if one is created.
// Returns 'true' if an object was actually created. // Returns 'true' if an object was actually created.
// Called at taint-time. // Called at taint-time.
private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape, private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape,
ShapeData shapeData, BodyDestructionCallback bodyCallback) ShapeData shapeData, BodyDestructionCallback bodyCallback)
{ {
bool ret = false; bool ret = false;
@ -701,6 +770,7 @@ public class BSShapeCollection : IDisposable
if (mustRebuild || forceRebuild) if (mustRebuild || forceRebuild)
{ {
// Free any old body
DereferenceBody(prim.BSBody, true, bodyCallback); DereferenceBody(prim.BSBody, true, bodyCallback);
BulletBody aBody; BulletBody aBody;
@ -709,13 +779,13 @@ public class BSShapeCollection : IDisposable
{ {
bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
shapeData.ID, shapeData.Position, shapeData.Rotation); shapeData.ID, shapeData.Position, shapeData.Rotation);
// DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
} }
else else
{ {
bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
shapeData.ID, shapeData.Position, shapeData.Rotation); shapeData.ID, shapeData.Position, shapeData.Rotation);
// DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
} }
aBody = new BulletBody(shapeData.ID, bodyPtr); aBody = new BulletBody(shapeData.ID, bodyPtr);
@ -731,7 +801,8 @@ public class BSShapeCollection : IDisposable
private void DetailLog(string msg, params Object[] args) private void DetailLog(string msg, params Object[] args)
{ {
PhysicsScene.PhysicsLogging.Write(msg, args); if (PhysicsScene.PhysicsLogging.Enabled)
PhysicsScene.DetailLog(msg, args);
} }
} }
} }

View File

@ -114,6 +114,9 @@ public class BSTerrainManager
BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
Vector3.Zero, Quaternion.Identity)); Vector3.Zero, Quaternion.Identity));
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr); BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr);
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
// Ground plane does not move
BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
// Everything collides with the ground plane. // Everything collides with the ground plane.
BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr,
(uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask);
@ -201,10 +204,10 @@ public class BSTerrainManager
// The 'doNow' boolean says whether to do all the unmanaged activities right now (like when // The 'doNow' boolean says whether to do all the unmanaged activities right now (like when
// calling this routine from initialization or taint-time routines) or whether to delay // calling this routine from initialization or taint-time routines) or whether to delay
// all the unmanaged activities to taint-time. // all the unmanaged activities to taint-time.
private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool atTaintTime) private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
{ {
DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},atTaintTime={3}", DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
BSScene.DetailLogZero, minCoords, maxCoords, atTaintTime); BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
float minZ = float.MaxValue; float minZ = float.MaxValue;
float maxZ = float.MinValue; float maxZ = float.MinValue;
@ -296,16 +299,16 @@ public class BSTerrainManager
mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID, mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID,
mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN); mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
// Create the terrain shape from the mapInfo
mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
ShapeData.PhysicsShapeType.SHAPE_TERRAIN);
// The terrain object initial position is at the center of the object // The terrain object initial position is at the center of the object
Vector3 centerPos; Vector3 centerPos;
centerPos.X = minCoords.X + (mapInfo.sizeX / 2f); centerPos.X = minCoords.X + (mapInfo.sizeX / 2f);
centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f); centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f);
centerPos.Z = minZ + ((maxZ - minZ) / 2f); centerPos.Z = minZ + ((maxZ - minZ) / 2f);
// Create the terrain shape from the mapInfo
mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
ShapeData.PhysicsShapeType.SHAPE_TERRAIN);
mapInfo.terrainBody = new BulletBody(mapInfo.ID, mapInfo.terrainBody = new BulletBody(mapInfo.ID,
BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr, BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr,
id, centerPos, Quaternion.Identity)); id, centerPos, Quaternion.Identity));
@ -320,9 +323,6 @@ public class BSTerrainManager
BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution); BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT); BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
BulletSimAPI.SetMassProps2(mapInfo.terrainBody.ptr, 0f, Vector3.Zero);
BulletSimAPI.UpdateInertiaTensor2(mapInfo.terrainBody.ptr);
// Return the new terrain to the world of physical objects // Return the new terrain to the world of physical objects
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr); BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
@ -335,14 +335,15 @@ public class BSTerrainManager
// Make sure the new shape is processed. // Make sure the new shape is processed.
// BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true); // BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true);
BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION); BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING);
// BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
m_terrainModified = true; m_terrainModified = true;
}; };
// There is the option to do the changes now (we're already in 'taint time'), or // There is the option to do the changes now (we're already in 'taint time'), or
// to do the Bullet operations later. // to do the Bullet operations later.
if (atTaintTime) if (inTaintTime)
rebuildOperation(); rebuildOperation();
else else
PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation); PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation);
@ -381,7 +382,7 @@ public class BSTerrainManager
}; };
// If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time. // If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time.
if (atTaintTime) if (inTaintTime)
createOperation(); createOperation();
else else
PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation); PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation);

View File

@ -101,9 +101,8 @@ public struct BulletShape
} }
public IntPtr ptr; public IntPtr ptr;
public ShapeData.PhysicsShapeType type; public ShapeData.PhysicsShapeType type;
public ulong shapeKey; public System.UInt64 shapeKey;
public bool isNativeShape; public bool isNativeShape;
// Hulls have an underlying mesh. A pointer to it is hidden here.
public override string ToString() public override string ToString()
{ {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
@ -192,8 +191,9 @@ public struct ShapeData
SHAPE_SPHERE = 5, SHAPE_SPHERE = 5,
SHAPE_MESH = 6, SHAPE_MESH = 6,
SHAPE_HULL = 7, SHAPE_HULL = 7,
SHAPE_GROUNDPLANE = 8, // following defined by BulletSim
SHAPE_TERRAIN = 9, SHAPE_GROUNDPLANE = 20,
SHAPE_TERRAIN = 21,
}; };
public uint ID; public uint ID;
public PhysicsShapeType Type; public PhysicsShapeType Type;
@ -223,6 +223,7 @@ public struct ShapeData
KEY_SPHERE = 2, KEY_SPHERE = 2,
KEY_CONE = 3, KEY_CONE = 3,
KEY_CYLINDER = 4, KEY_CYLINDER = 4,
KEY_CAPSULE = 5,
} }
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
@ -282,6 +283,7 @@ public struct ConfigurationParameters
public float terrainHitFraction; public float terrainHitFraction;
public float terrainRestitution; public float terrainRestitution;
public float avatarFriction; public float avatarFriction;
public float avatarStandingFriction;
public float avatarDensity; public float avatarDensity;
public float avatarRestitution; public float avatarRestitution;
public float avatarCapsuleRadius; public float avatarCapsuleRadius;
@ -305,6 +307,8 @@ public struct ConfigurationParameters
public float linkConstraintCFM; public float linkConstraintCFM;
public float linkConstraintSolverIterations; public float linkConstraintSolverIterations;
public float physicsLoggingFrames;
public const float numericTrue = 1f; public const float numericTrue = 1f;
public const float numericFalse = 0f; public const float numericFalse = 0f;
} }
@ -386,7 +390,7 @@ public enum CollisionFilterGroups : uint
VolumeDetectMask = ~BSensorTrigger, VolumeDetectMask = ~BSensorTrigger,
TerrainFilter = BTerrainFilter, TerrainFilter = BTerrainFilter,
TerrainMask = BAllFilter & ~BStaticFilter, TerrainMask = BAllFilter & ~BStaticFilter,
GroundPlaneFilter = BAllFilter, GroundPlaneFilter = BGroundPlaneFilter,
GroundPlaneMask = BAllFilter GroundPlaneMask = BAllFilter
}; };
@ -426,6 +430,7 @@ public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg
[return: MarshalAs(UnmanagedType.LPStr)] [return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetVersion(); public static extern string GetVersion();
/* Remove the linkage to the old api methods
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern uint Initialize(Vector3 maxPosition, IntPtr parms, public static extern uint Initialize(Vector3 maxPosition, IntPtr parms,
int maxCollisions, IntPtr collisionArray, int maxCollisions, IntPtr collisionArray,
@ -529,7 +534,7 @@ public static extern Vector3 RecoverFromPenetration(uint worldID, uint id);
// =============================================================================== // ===============================================================================
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpBulletStatistics(); public static extern void DumpBulletStatistics();
*/
// Log a debug message // Log a debug message
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetDebugLogCallback(DebugLogCallback callback); public static extern void SetDebugLogCallback(DebugLogCallback callback);
@ -560,7 +565,8 @@ public static extern IntPtr GetBodyHandle2(IntPtr world, uint id);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms, public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms,
int maxCollisions, IntPtr collisionArray, int maxCollisions, IntPtr collisionArray,
int maxUpdates, IntPtr updateArray); int maxUpdates, IntPtr updateArray,
DebugLogCallback logRoutine);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool UpdateParameter2(IntPtr world, uint localID, String parm, float value); public static extern bool UpdateParameter2(IntPtr world, uint localID, String parm, float value);
@ -601,6 +607,9 @@ public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData)
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsNativeShape2(IntPtr shape); public static extern bool IsNativeShape2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateCompoundShape2(IntPtr sim); public static extern IntPtr CreateCompoundShape2(IntPtr sim);
@ -1036,18 +1045,6 @@ public static extern IntPtr GetConstraintRef2(IntPtr obj, int index);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern int GetNumConstraintRefs2(IntPtr obj); public static extern int GetNumConstraintRefs2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetDeltaLinearVelocity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetDeltaAngularVelocity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetPushVelocity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetTurnVelocity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask); public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask);
@ -1107,6 +1104,15 @@ public static extern float GetMargin2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject); public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpCollisionShape2(IntPtr sim, IntPtr collisionShape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpConstraint2(IntPtr sim, IntPtr constrain);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpAllInfo2(IntPtr sim);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo); public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);

View File

@ -76,7 +76,7 @@ namespace OpenSim.Region.Physics.Manager
get { return new NullPhysicsScene(); } get { return new NullPhysicsScene(); }
} }
public RequestAssetDelegate RequestAssetMethod { private get; set; } public RequestAssetDelegate RequestAssetMethod { get; set; }
public virtual void TriggerPhysicsBasedRestart() public virtual void TriggerPhysicsBasedRestart()
{ {

View File

@ -66,6 +66,14 @@ namespace OpenSim.Region.Physics.OdePlugin
public int ExpectedCollisionContacts { get { return m_expectedCollisionContacts; } } public int ExpectedCollisionContacts { get { return m_expectedCollisionContacts; } }
private int m_expectedCollisionContacts = 0; private int m_expectedCollisionContacts = 0;
/// <summary>
/// Gets collide bits so that we can still perform land collisions if a mesh fails to load.
/// </summary>
private int BadMeshAssetCollideBits
{
get { return m_isphysical ? (int)CollisionCategories.Land : 0; }
}
/// <summary> /// <summary>
/// Is this prim subject to physics? Even if not, it's still solid for collision purposes. /// Is this prim subject to physics? Even if not, it's still solid for collision purposes.
/// </summary> /// </summary>
@ -100,6 +108,9 @@ namespace OpenSim.Region.Physics.OdePlugin
private Vector3 m_taintAngularLock = Vector3.One; private Vector3 m_taintAngularLock = Vector3.One;
private IntPtr Amotor = IntPtr.Zero; private IntPtr Amotor = IntPtr.Zero;
private object m_assetsLock = new object();
private bool m_assetFailed = false;
private Vector3 m_PIDTarget; private Vector3 m_PIDTarget;
private float m_PIDTau; private float m_PIDTau;
private float PID_D = 35f; private float PID_D = 35f;
@ -282,6 +293,7 @@ namespace OpenSim.Region.Physics.OdePlugin
} }
m_taintadd = true; m_taintadd = true;
m_assetFailed = false;
_parent_scene.AddPhysicsActorTaint(this); _parent_scene.AddPhysicsActorTaint(this);
} }
@ -337,8 +349,16 @@ namespace OpenSim.Region.Physics.OdePlugin
prim_geom = geom; prim_geom = geom;
//Console.WriteLine("SetGeom to " + prim_geom + " for " + Name); //Console.WriteLine("SetGeom to " + prim_geom + " for " + Name);
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); if (m_assetFailed)
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); {
d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
}
else
{
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
}
_parent_scene.geom_name_map[prim_geom] = Name; _parent_scene.geom_name_map[prim_geom] = Name;
_parent_scene.actor_name_map[prim_geom] = this; _parent_scene.actor_name_map[prim_geom] = this;
@ -401,8 +421,17 @@ namespace OpenSim.Region.Physics.OdePlugin
myrot.W = _orientation.W; myrot.W = _orientation.W;
d.BodySetQuaternion(Body, ref myrot); d.BodySetQuaternion(Body, ref myrot);
d.GeomSetBody(prim_geom, Body); d.GeomSetBody(prim_geom, Body);
m_collisionCategories |= CollisionCategories.Body;
m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); if (m_assetFailed)
{
d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
}
else
{
m_collisionCategories |= CollisionCategories.Body;
m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
}
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
@ -774,8 +803,16 @@ namespace OpenSim.Region.Physics.OdePlugin
m_collisionCategories &= ~CollisionCategories.Body; m_collisionCategories &= ~CollisionCategories.Body;
m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land); m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); if (m_assetFailed)
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); {
d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, 0);
}
else
{
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
}
d.BodyDestroy(Body); d.BodyDestroy(Body);
lock (childrenPrim) lock (childrenPrim)
@ -799,8 +836,17 @@ namespace OpenSim.Region.Physics.OdePlugin
m_collisionCategories &= ~CollisionCategories.Body; m_collisionCategories &= ~CollisionCategories.Body;
m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land); m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); if (m_assetFailed)
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); {
d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, 0);
}
else
{
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
}
Body = IntPtr.Zero; Body = IntPtr.Zero;
} }
@ -1090,8 +1136,16 @@ Console.WriteLine("ZProcessTaints for " + Name);
prm.m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); prm.m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
//Console.WriteLine(" GeomSetCategoryBits 1: " + prm.prim_geom + " - " + (int)prm.m_collisionCategories + " for " + Name); //Console.WriteLine(" GeomSetCategoryBits 1: " + prm.prim_geom + " - " + (int)prm.m_collisionCategories + " for " + Name);
d.GeomSetCategoryBits(prm.prim_geom, (int)prm.m_collisionCategories); if (prm.m_assetFailed)
d.GeomSetCollideBits(prm.prim_geom, (int)prm.m_collisionFlags); {
d.GeomSetCategoryBits(prm.prim_geom, 0);
d.GeomSetCollideBits(prm.prim_geom, prm.BadMeshAssetCollideBits);
}
else
{
d.GeomSetCategoryBits(prm.prim_geom, (int)prm.m_collisionCategories);
d.GeomSetCollideBits(prm.prim_geom, (int)prm.m_collisionFlags);
}
d.Quaternion quat = new d.Quaternion(); d.Quaternion quat = new d.Quaternion();
quat.W = prm._orientation.W; quat.W = prm._orientation.W;
@ -1136,10 +1190,18 @@ Console.WriteLine("ZProcessTaints for " + Name);
m_collisionCategories |= CollisionCategories.Body; m_collisionCategories |= CollisionCategories.Body;
m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
//Console.WriteLine("GeomSetCategoryBits 2: " + prim_geom + " - " + (int)m_collisionCategories + " for " + Name); if (m_assetFailed)
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); {
//Console.WriteLine(" Post GeomSetCategoryBits 2"); d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
}
else
{
//Console.WriteLine("GeomSetCategoryBits 2: " + prim_geom + " - " + (int)m_collisionCategories + " for " + Name);
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
//Console.WriteLine(" Post GeomSetCategoryBits 2");
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
}
d.Quaternion quat2 = new d.Quaternion(); d.Quaternion quat2 = new d.Quaternion();
quat2.W = _orientation.W; quat2.W = _orientation.W;
@ -1300,8 +1362,16 @@ Console.WriteLine("ZProcessTaints for " + Name);
disableBodySoft(); disableBodySoft();
} }
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); if (m_assetFailed)
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); {
d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, 0);
}
else
{
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
}
if (IsPhysical) if (IsPhysical)
{ {
@ -1322,8 +1392,16 @@ Console.WriteLine("ZProcessTaints for " + Name);
if (m_collidesWater) if (m_collidesWater)
m_collisionFlags |= CollisionCategories.Water; m_collisionFlags |= CollisionCategories.Water;
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); if (m_assetFailed)
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); {
d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
}
else
{
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
}
if (IsPhysical) if (IsPhysical)
{ {
@ -1498,6 +1576,10 @@ Console.WriteLine("CreateGeom:");
mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, _parent_scene.meshSculptLOD, IsPhysical); mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, _parent_scene.meshSculptLOD, IsPhysical);
// createmesh returns null when it's a shape that isn't a cube. // createmesh returns null when it's a shape that isn't a cube.
// m_log.Debug(m_localID); // m_log.Debug(m_localID);
if (mesh == null)
CheckMeshAsset();
else
m_assetFailed = false;
} }
#if SPAM #if SPAM
@ -1997,7 +2079,14 @@ Console.WriteLine(" JointCreateFixed");
// Don't need to re-enable body.. it's done in SetMesh // Don't need to re-enable body.. it's done in SetMesh
if (_parent_scene.needsMeshing(_pbs)) if (_parent_scene.needsMeshing(_pbs))
{
mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical); mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical);
if (mesh == null)
CheckMeshAsset();
else
m_assetFailed = false;
}
} }
CreateGeom(m_targetSpace, mesh); CreateGeom(m_targetSpace, mesh);
@ -2049,14 +2138,19 @@ Console.WriteLine(" JointCreateFixed");
m_collisionFlags &= ~CollisionCategories.Water; m_collisionFlags &= ~CollisionCategories.Water;
} }
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); if (m_assetFailed)
} d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
else
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
}
/// <summary> /// <summary>
/// Change prim in response to a shape taint. /// Change prim in response to a shape taint.
/// </summary> /// </summary>
private void changeshape() private void changeshape()
{ {
m_taintshape = false;
// Cleanup of old prim geometry and Bodies // Cleanup of old prim geometry and Bodies
if (IsPhysical && Body != IntPtr.Zero) if (IsPhysical && Body != IntPtr.Zero)
{ {
@ -2084,6 +2178,7 @@ Console.WriteLine(" JointCreateFixed");
IMesh mesh = null; IMesh mesh = null;
if (_parent_scene.needsMeshing(_pbs)) if (_parent_scene.needsMeshing(_pbs))
{ {
// Don't need to re-enable body.. it's done in CreateMesh // Don't need to re-enable body.. it's done in CreateMesh
@ -2094,6 +2189,10 @@ Console.WriteLine(" JointCreateFixed");
// createmesh returns null when it doesn't mesh. // createmesh returns null when it doesn't mesh.
mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical); mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical);
if (mesh == null)
CheckMeshAsset();
else
m_assetFailed = false;
} }
CreateGeom(m_targetSpace, mesh); CreateGeom(m_targetSpace, mesh);
@ -2130,7 +2229,7 @@ Console.WriteLine(" JointCreateFixed");
} }
resetCollisionAccounting(); resetCollisionAccounting();
m_taintshape = false; // m_taintshape = false;
} }
/// <summary> /// <summary>
@ -2396,6 +2495,7 @@ Console.WriteLine(" JointCreateFixed");
set set
{ {
_pbs = value; _pbs = value;
m_assetFailed = false;
m_taintshape = true; m_taintshape = true;
} }
} }
@ -3234,5 +3334,37 @@ Console.WriteLine(" JointCreateFixed");
{ {
m_material = pMaterial; m_material = pMaterial;
} }
private void CheckMeshAsset()
{
if (_pbs.SculptEntry && !m_assetFailed && _pbs.SculptTexture != UUID.Zero)
{
m_assetFailed = true;
Util.FireAndForget(delegate
{
RequestAssetDelegate assetProvider = _parent_scene.RequestAssetMethod;
if (assetProvider != null)
assetProvider(_pbs.SculptTexture, MeshAssetReveived);
});
}
}
void MeshAssetReveived(AssetBase asset)
{
if (asset.Data != null && asset.Data.Length > 0)
{
if (!_pbs.SculptEntry)
return;
if (_pbs.SculptTexture.ToString() != asset.ID)
return;
_pbs.SculptData = new byte[asset.Data.Length];
asset.Data.CopyTo(_pbs.SculptData, 0);
// m_assetFailed = false;
m_taintshape = true;
_parent_scene.AddPhysicsActorTaint(this);
}
}
} }
} }

View File

@ -4320,4 +4320,4 @@ namespace OpenSim.Region.Physics.OdePlugin
m_stats[ODEPrimUpdateFrameMsStatName] = 0; m_stats[ODEPrimUpdateFrameMsStatName] = 0;
} }
} }
} }

View File

@ -56,6 +56,7 @@ using GridRegion = OpenSim.Services.Interfaces.GridRegion;
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
using PrimType = OpenSim.Region.Framework.Scenes.PrimType; using PrimType = OpenSim.Region.Framework.Scenes.PrimType;
using AssetLandmark = OpenSim.Framework.AssetLandmark; using AssetLandmark = OpenSim.Framework.AssetLandmark;
using RegionFlags = OpenSim.Framework.RegionFlags;
using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
@ -3772,6 +3773,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
} }
/// <summary> /// <summary>
/// Returns the name of the child prim or seated avatar matching the
/// specified link number.
/// </summary>
/// <param name="linknum">
/// The number of a link in the linkset or a link-related constant.
/// </param>
/// <returns>
/// The name determined to match the specified link number.
/// </returns>
/// <remarks>
/// The rules governing the returned name are not simple. The only /// The rules governing the returned name are not simple. The only
/// time a blank name is returned is if the target prim has a blank /// time a blank name is returned is if the target prim has a blank
/// name. If no prim with the given link number can be found then /// name. If no prim with the given link number can be found then
@ -3799,10 +3810,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
/// Mentions NULL_KEY being returned /// Mentions NULL_KEY being returned
/// http://wiki.secondlife.com/wiki/LlGetLinkName /// http://wiki.secondlife.com/wiki/LlGetLinkName
/// Mentions using the LINK_* constants, some of which are negative /// Mentions using the LINK_* constants, some of which are negative
/// </summary> /// </remarks>
public LSL_String llGetLinkName(int linknum) public LSL_String llGetLinkName(int linknum)
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
// simplest case, this prims link number
if (linknum == m_host.LinkNum || linknum == ScriptBaseClass.LINK_THIS)
return m_host.Name;
// parse for sitting avatare-names // parse for sitting avatare-names
List<String> nametable = new List<String>(); List<String> nametable = new List<String>();
World.ForEachRootScenePresence(delegate(ScenePresence presence) World.ForEachRootScenePresence(delegate(ScenePresence presence)
@ -3826,10 +3841,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
return nametable[totalprims - linknum]; return nametable[totalprims - linknum];
} }
// simplest case, this prims link number
if (m_host.LinkNum == linknum)
return m_host.Name;
// Single prim // Single prim
if (m_host.LinkNum == 0) if (m_host.LinkNum == 0)
{ {
@ -6458,7 +6469,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
GridInstantMessage msg = new GridInstantMessage(World, GridInstantMessage msg = new GridInstantMessage(World,
m_host.OwnerID, m_host.Name, destID, m_host.OwnerID, m_host.Name, destID,
(byte)InstantMessageDialog.TaskInventoryOffered, (byte)InstantMessageDialog.TaskInventoryOffered,
false, string.Format("'{0}'"), false, string.Format("'{0}'", category),
// We won't go so far as to add a SLURL, but this is the format used by LL as of 2012-10-06 // We won't go so far as to add a SLURL, but this is the format used by LL as of 2012-10-06
// false, string.Format("'{0}' ( http://slurl.com/secondlife/{1}/{2}/{3}/{4} )", category, World.Name, (int)pos.X, (int)pos.Y, (int)pos.Z), // false, string.Format("'{0}' ( http://slurl.com/secondlife/{1}/{2}/{3}/{4} )", category, World.Name, (int)pos.X, (int)pos.Y, (int)pos.Z),
folderID, false, pos, folderID, false, pos,
@ -9313,11 +9324,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
GridRegion info; GridRegion info;
if (m_ScriptEngine.World.RegionInfo.RegionName == simulator) //Det data for this simulator? if (World.RegionInfo.RegionName == simulator)
info = new GridRegion(World.RegionInfo);
info = new GridRegion(m_ScriptEngine.World.RegionInfo);
else else
info = m_ScriptEngine.World.GridService.GetRegionByName(m_ScriptEngine.World.RegionInfo.ScopeID, simulator); info = World.GridService.GetRegionByName(m_ScriptEngine.World.RegionInfo.ScopeID, simulator);
switch (data) switch (data)
{ {
@ -9327,9 +9337,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
ScriptSleep(1000); ScriptSleep(1000);
return UUID.Zero.ToString(); return UUID.Zero.ToString();
} }
if (m_ScriptEngine.World.RegionInfo.RegionName != simulator)
bool isHypergridRegion = false;
if (World.RegionInfo.RegionName != simulator && info.RegionSecret != "")
{
// Hypergrid is currently placing real destination region co-ords into RegionSecret.
// But other code can also use this field for a genuine RegionSecret! Therefore, if
// anything is present we need to disambiguate.
//
// FIXME: Hypergrid should be storing this data in a different field.
RegionFlags regionFlags
= (RegionFlags)m_ScriptEngine.World.GridService.GetRegionFlags(
info.ScopeID, info.RegionID);
isHypergridRegion = (regionFlags & RegionFlags.Hyperlink) != 0;
}
if (isHypergridRegion)
{ {
//Hypergrid Region co-ordinates
uint rx = 0, ry = 0; uint rx = 0, ry = 0;
Utils.LongToUInts(Convert.ToUInt64(info.RegionSecret), out rx, out ry); Utils.LongToUInts(Convert.ToUInt64(info.RegionSecret), out rx, out ry);
@ -9340,7 +9365,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
} }
else else
{ {
//Local-cooridnates // Local grid co-oridnates
reply = new LSL_Vector( reply = new LSL_Vector(
info.RegionLocX, info.RegionLocX,
info.RegionLocY, info.RegionLocY,

View File

@ -3545,17 +3545,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
/// <returns></returns> /// <returns></returns>
public void osSetContentType(LSL_Key id, string type) public void osSetContentType(LSL_Key id, string type)
{ {
CheckThreatLevel(ThreatLevel.High,"osSetResponseType"); CheckThreatLevel(ThreatLevel.High, "osSetContentType");
if (m_UrlModule != null) if (m_UrlModule != null)
m_UrlModule.HttpContentType(new UUID(id),type); m_UrlModule.HttpContentType(new UUID(id),type);
} }
/// Shout an error if the object owner did not grant the script the specified permissions. /// Shout an error if the object owner did not grant the script the specified permissions.
/// </summary> /// </summary>
/// <param name="perms"></param> /// <param name="perms"></param>
/// <returns>boolean indicating whether an error was shouted.</returns> /// <returns>boolean indicating whether an error was shouted.</returns>
protected bool ShoutErrorOnLackingOwnerPerms(int perms, string errorPrefix) protected bool ShoutErrorOnLackingOwnerPerms(int perms, string errorPrefix)
{ {
CheckThreatLevel(ThreatLevel.Moderate, "osDropAttachment");
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
bool fail = false; bool fail = false;
if (m_item.PermsGranter != m_host.OwnerID) if (m_item.PermsGranter != m_host.OwnerID)

View File

@ -612,6 +612,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
public const int CLICK_ACTION_OPEN = 4; public const int CLICK_ACTION_OPEN = 4;
public const int CLICK_ACTION_PLAY = 5; public const int CLICK_ACTION_PLAY = 5;
public const int CLICK_ACTION_OPEN_MEDIA = 6; public const int CLICK_ACTION_OPEN_MEDIA = 6;
public const int CLICK_ACTION_ZOOM = 7;
// constants for the llDetectedTouch* functions // constants for the llDetectedTouch* functions
public const int TOUCH_INVALID_FACE = -1; public const int TOUCH_INVALID_FACE = -1;

View File

@ -128,7 +128,7 @@ namespace OpenSim.Services.Connectors.Friends
return Call(region, sendData); return Call(region, sendData);
} }
public bool StatusNotify(GridRegion region, UUID userID, UUID friendID, bool online) public bool StatusNotify(GridRegion region, UUID userID, string friendID, bool online)
{ {
Dictionary<string, object> sendData = new Dictionary<string, object>(); Dictionary<string, object> sendData = new Dictionary<string, object>();
//sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString();
@ -136,7 +136,7 @@ namespace OpenSim.Services.Connectors.Friends
sendData["METHOD"] = "status"; sendData["METHOD"] = "status";
sendData["FromID"] = userID.ToString(); sendData["FromID"] = userID.ToString();
sendData["ToID"] = friendID.ToString(); sendData["ToID"] = friendID;
sendData["Online"] = online.ToString(); sendData["Online"] = online.ToString();
return Call(region, sendData); return Call(region, sendData);
@ -153,7 +153,7 @@ namespace OpenSim.Services.Connectors.Friends
if (!region.ServerURI.EndsWith("/")) if (!region.ServerURI.EndsWith("/"))
path = "/" + path; path = "/" + path;
string uri = region.ServerURI + path; string uri = region.ServerURI + path;
m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: calling {0}", uri); // m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: calling {0}", uri);
try try
{ {

View File

@ -397,7 +397,7 @@ namespace OpenSim.Services.HypergridService
if (region != null) if (region != null)
{ {
m_log.DebugFormat("[HGFRIENDS SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline")); m_log.DebugFormat("[HGFRIENDS SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline"));
m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID, online); m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online);
} }
} }
} }

View File

@ -504,7 +504,7 @@ namespace OpenSim.Services.HypergridService
if (region != null) if (region != null)
{ {
m_log.DebugFormat("[USER AGENT SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline")); m_log.DebugFormat("[USER AGENT SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline"));
m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID, online); m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online);
} }
} }
} }

View File

@ -61,13 +61,49 @@ namespace OpenSim.Services.Interfaces
public interface IPresenceService public interface IPresenceService
{ {
/// <summary>
/// Store session information.
/// </summary>
/// <returns>/returns>
/// <param name='userID'></param>
/// <param name='sessionID'></param>
/// <param name='secureSessionID'></param>
bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID); bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID);
/// <summary>
/// Remove session information.
/// </summary>
/// <returns></returns>
/// <param name='sessionID'></param>
bool LogoutAgent(UUID sessionID); bool LogoutAgent(UUID sessionID);
/// <summary>
/// Remove session information for all agents in the given region.
/// </summary>
/// <returns></returns>
/// <param name='regionID'></param>
bool LogoutRegionAgents(UUID regionID); bool LogoutRegionAgents(UUID regionID);
/// <summary>
/// Update data for an existing session.
/// </summary>
/// <returns></returns>
/// <param name='sessionID'></param>
/// <param name='regionID'></param>
bool ReportAgent(UUID sessionID, UUID regionID); bool ReportAgent(UUID sessionID, UUID regionID);
/// <summary>
/// Get session information for a given session ID.
/// </summary>
/// <returns></returns>
/// <param name='sessionID'></param>
PresenceInfo GetAgent(UUID sessionID); PresenceInfo GetAgent(UUID sessionID);
/// <summary>
/// Get session information for a collection of users.
/// </summary>
/// <returns>Session information for the users.</returns>
/// <param name='userIDs'></param>
PresenceInfo[] GetAgents(string[] userIDs); PresenceInfo[] GetAgents(string[] userIDs);
} }
} }

View File

@ -1577,6 +1577,11 @@
MessagingModule = GroupsMessagingModule MessagingModule = GroupsMessagingModule
;MessagingEnabled = true ;MessagingEnabled = true
; Experimental option to only message cached online users rather than all users
; Should make large group with few online members messaging faster, as the expense of more calls to ROBUST presence service
; This currently only applies to the Flotsam XmlRpc backend
MessageOnlineUsersOnly = false
; Service connectors to the Groups Service. Select one depending on whether you're using a Flotsam XmlRpc backend or a SimianGrid backend ; Service connectors to the Groups Service. Select one depending on whether you're using a Flotsam XmlRpc backend or a SimianGrid backend
; SimianGrid Service for Groups ; SimianGrid Service for Groups
@ -1602,10 +1607,14 @@
[PacketPool] [PacketPool]
; Enables the experimental packet pool. Yes, we've been here before.
;RecyclePackets = true; ;RecyclePackets = true;
;RecycleDataBlocks = true; ;RecycleDataBlocks = true;
; If true, then the basic packet objects used to receive data are also recycled, not just the LLUDP packets.
; This reduces data churn
; This setting is currently experimental and defaults to false.
RecycleBaseUDPPackets = false;
[InterestManagement] [InterestManagement]
; This section controls how state updates are prioritized for each client ; This section controls how state updates are prioritized for each client

View File

@ -21,7 +21,38 @@
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>] ; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
; * ; *
[Startup] [Startup]
ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:XInventoryInConnector,8004/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector,8002/OpenSim.Server.Handlers.dll:GatekeeperServiceInConnector,8002/OpenSim.Server.Handlers.dll:UserAgentServerConnector,HGInventoryService@8002/OpenSim.Server.Handlers.dll:XInventoryInConnector,HGAssetService@8002/OpenSim.Server.Handlers.dll:AssetServiceConnector,8002/OpenSim.Server.Handlers.dll:HeloServiceInConnector,8002/OpenSim.Server.Handlers.dll:HGFriendsServerConnector,8002/OpenSim.Server.Handlers.dll:InstantMessageServerConnector,8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector,8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"
[ServiceList]
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"
InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector"
VoiceConnector = "8004/OpenSim.Server.Handlers.dll:FreeswitchServerConnector"
GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector"
GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector"
AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector"
OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector"
AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector"
LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector"
PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector"
UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector"
GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector"
FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector"
MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector"
MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector"
;; Additions for Hypergrid
GatekeeperServiceInConnector = "8002/OpenSim.Server.Handlers.dll:GatekeeperServiceInConnector"
UserAgentServerConnector = "8002/OpenSim.Server.Handlers.dll:UserAgentServerConnector"
HeloServiceInConnector = "8002/OpenSim.Server.Handlers.dll:HeloServiceInConnector"
HGFriendsServerConnector = "8002/OpenSim.Server.Handlers.dll:HGFriendsServerConnector"
InstantMessageServerConnector = "8002/OpenSim.Server.Handlers.dll:InstantMessageServerConnector"
HGInventoryServiceConnector = "HGInventoryService@8002/OpenSim.Server.Handlers.dll:XInventoryInConnector"
HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:AssetServiceConnector"
;; Additions for other add-on modules. For example:
;; WifiServerConnector = "8002/Diva.Wifi.dll:WifiServerConnector"
; Plugin Registry Location ; Plugin Registry Location
; Set path to directory for plugin registry. Information ; Set path to directory for plugin registry. Information

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -113,6 +113,34 @@
</Files> </Files>
</Project> </Project>
<Project frameworkVersion="v3_5" name="OpenSim.Services.Interfaces" path="OpenSim/Services/Interfaces" type="Library">
<Configuration name="Debug">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<Configuration name="Release">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<ReferencePath>../../../bin/</ReferencePath>
<Reference name="System"/>
<Reference name="OpenMetaverseTypes" path="../../../bin/"/>
<Reference name="OpenMetaverse" path="../../../bin/"/>
<Reference name="OpenMetaverse.StructuredData" path="../../../bin/"/>
<Reference name="OpenSim.Framework"/>
<Reference name="OpenSim.Framework.Console"/>
<Reference name="OpenSim.Framework.Servers.HttpServer"/>
<Reference name="Nini" path="../../../bin/"/>
<Reference name="log4net" path="../../../bin/"/>
<Files>
<Match pattern="*.cs" recurse="true"/>
</Files>
</Project>
<Project frameworkVersion="v3_5" name="OpenSim.Framework.Monitoring" path="OpenSim/Framework/Monitoring" type="Library"> <Project frameworkVersion="v3_5" name="OpenSim.Framework.Monitoring" path="OpenSim/Framework/Monitoring" type="Library">
<Configuration name="Debug"> <Configuration name="Debug">
<Options> <Options>
@ -206,34 +234,6 @@
</Files> </Files>
</Project> </Project>
<Project frameworkVersion="v3_5" name="OpenSim.Services.Interfaces" path="OpenSim/Services/Interfaces" type="Library">
<Configuration name="Debug">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<Configuration name="Release">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<ReferencePath>../../../bin/</ReferencePath>
<Reference name="System"/>
<Reference name="OpenMetaverseTypes" path="../../../bin/"/>
<Reference name="OpenMetaverse" path="../../../bin/"/>
<Reference name="OpenMetaverse.StructuredData" path="../../../bin/"/>
<Reference name="OpenSim.Framework"/>
<Reference name="OpenSim.Framework.Console"/>
<Reference name="OpenSim.Framework.Servers.HttpServer"/>
<Reference name="Nini" path="../../../bin/"/>
<Reference name="log4net" path="../../../bin/"/>
<Files>
<Match pattern="*.cs" recurse="true"/>
</Files>
</Project>
<Project frameworkVersion="v3_5" name="OpenSim.Framework.Serialization" path="OpenSim/Framework/Serialization" type="Library"> <Project frameworkVersion="v3_5" name="OpenSim.Framework.Serialization" path="OpenSim/Framework/Serialization" type="Library">
<Configuration name="Debug"> <Configuration name="Debug">
<Options> <Options>