Merge branch 'master' into connector_plugin
commit
07f1d44174
|
@ -223,9 +223,9 @@ namespace OpenSim.Capabilities.Handlers
|
|||
// sending back the last byte instead of an error status
|
||||
if (start >= texture.Data.Length)
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}",
|
||||
texture.ID, start, texture.Data.Length);
|
||||
// m_log.DebugFormat(
|
||||
// "[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}",
|
||||
// texture.ID, start, texture.Data.Length);
|
||||
|
||||
// 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
|
||||
|
|
|
@ -34,7 +34,7 @@ using OpenMetaverse;
|
|||
|
||||
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 MaxRawConsoleVectorValue = "~";
|
||||
|
@ -107,7 +107,7 @@ public class ConsoleUtil
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -805,8 +805,23 @@ namespace OpenSim.Framework
|
|||
event Action<IClientAPI> OnRegionHandShakeReply;
|
||||
event GenericCall1 OnRequestWearables;
|
||||
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;
|
||||
|
||||
/// <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 AgentRequestSit OnAgentRequestSit;
|
||||
event AgentSit OnAgentSit;
|
||||
event AvatarPickerRequest OnAvatarPickerRequest;
|
||||
|
|
|
@ -49,8 +49,8 @@ namespace OpenSim.Framework
|
|||
// use only one serializer to give the runtime a chance to
|
||||
// optimize it (it won't do that if you use a new instance
|
||||
// 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 _AABBMin = new Vector3();
|
||||
private int _area = 0;
|
||||
|
@ -65,11 +65,11 @@ namespace OpenSim.Framework
|
|||
private byte[] _bitmap = new byte[512];
|
||||
private string _description = String.Empty;
|
||||
|
||||
private uint _flags = (uint) ParcelFlags.AllowFly | (uint) ParcelFlags.AllowLandmark |
|
||||
(uint) ParcelFlags.AllowAPrimitiveEntry |
|
||||
(uint) ParcelFlags.AllowDeedToGroup | (uint) ParcelFlags.AllowTerraform |
|
||||
(uint) ParcelFlags.CreateObjects | (uint) ParcelFlags.AllowOtherScripts |
|
||||
(uint) ParcelFlags.SoundLocal | (uint) ParcelFlags.AllowVoiceChat;
|
||||
private uint _flags = (uint)ParcelFlags.AllowFly | (uint)ParcelFlags.AllowLandmark |
|
||||
(uint)ParcelFlags.AllowAPrimitiveEntry |
|
||||
(uint)ParcelFlags.AllowDeedToGroup | (uint)ParcelFlags.AllowTerraform |
|
||||
(uint)ParcelFlags.CreateObjects | (uint)ParcelFlags.AllowOtherScripts |
|
||||
(uint)ParcelFlags.SoundLocal | (uint)ParcelFlags.AllowVoiceChat;
|
||||
|
||||
private byte _landingType = 0;
|
||||
private string _name = "Your Parcel";
|
||||
|
@ -97,16 +97,36 @@ namespace OpenSim.Framework
|
|||
private bool _mediaLoop = false;
|
||||
private bool _obscureMusic = 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>
|
||||
/// Whether to obscure parcel media URL
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public bool ObscureMedia {
|
||||
get {
|
||||
public bool ObscureMedia
|
||||
{
|
||||
get
|
||||
{
|
||||
return _obscureMedia;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_obscureMedia = value;
|
||||
}
|
||||
}
|
||||
|
@ -115,11 +135,14 @@ namespace OpenSim.Framework
|
|||
/// Whether to obscure parcel music URL
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public bool ObscureMusic {
|
||||
get {
|
||||
public bool ObscureMusic
|
||||
{
|
||||
get
|
||||
{
|
||||
return _obscureMusic;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_obscureMusic = value;
|
||||
}
|
||||
}
|
||||
|
@ -128,11 +151,14 @@ namespace OpenSim.Framework
|
|||
/// Whether to loop parcel media
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public bool MediaLoop {
|
||||
get {
|
||||
public bool MediaLoop
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mediaLoop;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_mediaLoop = value;
|
||||
}
|
||||
}
|
||||
|
@ -141,11 +167,14 @@ namespace OpenSim.Framework
|
|||
/// Height of parcel media render
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public int MediaHeight {
|
||||
get {
|
||||
public int MediaHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mediaHeight;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_mediaHeight = value;
|
||||
}
|
||||
}
|
||||
|
@ -154,11 +183,14 @@ namespace OpenSim.Framework
|
|||
/// Width of parcel media render
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public int MediaWidth {
|
||||
get {
|
||||
public int MediaWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mediaWidth;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_mediaWidth = value;
|
||||
}
|
||||
}
|
||||
|
@ -167,11 +199,14 @@ namespace OpenSim.Framework
|
|||
/// Upper corner of the AABB for the parcel
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public Vector3 AABBMax {
|
||||
get {
|
||||
public Vector3 AABBMax
|
||||
{
|
||||
get
|
||||
{
|
||||
return _AABBMax;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_AABBMax = value;
|
||||
}
|
||||
}
|
||||
|
@ -179,11 +214,14 @@ namespace OpenSim.Framework
|
|||
/// Lower corner of the AABB for the parcel
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public Vector3 AABBMin {
|
||||
get {
|
||||
public Vector3 AABBMin
|
||||
{
|
||||
get
|
||||
{
|
||||
return _AABBMin;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_AABBMin = value;
|
||||
}
|
||||
}
|
||||
|
@ -191,11 +229,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Area in meters^2 the parcel contains
|
||||
/// </summary>
|
||||
public int Area {
|
||||
get {
|
||||
public int Area
|
||||
{
|
||||
get
|
||||
{
|
||||
return _area;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_area = value;
|
||||
}
|
||||
}
|
||||
|
@ -203,11 +244,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// ID of auction (3rd Party Integration) when parcel is being auctioned
|
||||
/// </summary>
|
||||
public uint AuctionID {
|
||||
get {
|
||||
public uint AuctionID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _auctionID;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_auctionID = value;
|
||||
}
|
||||
}
|
||||
|
@ -215,11 +259,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// UUID of authorized buyer of parcel. This is UUID.Zero if anyone can buy it.
|
||||
/// </summary>
|
||||
public UUID AuthBuyerID {
|
||||
get {
|
||||
public UUID AuthBuyerID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _authBuyerID;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_authBuyerID = value;
|
||||
}
|
||||
}
|
||||
|
@ -227,11 +274,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Category of parcel. Used for classifying the parcel in classified listings
|
||||
/// </summary>
|
||||
public ParcelCategory Category {
|
||||
get {
|
||||
public ParcelCategory Category
|
||||
{
|
||||
get
|
||||
{
|
||||
return _category;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_category = value;
|
||||
}
|
||||
}
|
||||
|
@ -239,11 +289,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Date that the current owner purchased or claimed the parcel
|
||||
/// </summary>
|
||||
public int ClaimDate {
|
||||
get {
|
||||
public int ClaimDate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _claimDate;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_claimDate = value;
|
||||
}
|
||||
}
|
||||
|
@ -251,11 +304,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// The last price that the parcel was sold at
|
||||
/// </summary>
|
||||
public int ClaimPrice {
|
||||
get {
|
||||
public int ClaimPrice
|
||||
{
|
||||
get
|
||||
{
|
||||
return _claimPrice;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_claimPrice = value;
|
||||
}
|
||||
}
|
||||
|
@ -263,11 +319,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Global ID for the parcel. (3rd Party Integration)
|
||||
/// </summary>
|
||||
public UUID GlobalID {
|
||||
get {
|
||||
public UUID GlobalID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _globalID;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_globalID = value;
|
||||
}
|
||||
}
|
||||
|
@ -275,11 +334,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Unique ID of the Group that owns
|
||||
/// </summary>
|
||||
public UUID GroupID {
|
||||
get {
|
||||
public UUID GroupID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _groupID;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_groupID = value;
|
||||
}
|
||||
}
|
||||
|
@ -287,11 +349,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Returns true if the Land Parcel is owned by a group
|
||||
/// </summary>
|
||||
public bool IsGroupOwned {
|
||||
get {
|
||||
public bool IsGroupOwned
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isGroupOwned;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_isGroupOwned = value;
|
||||
}
|
||||
}
|
||||
|
@ -299,11 +364,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// jp2 data for the image representative of the parcel in the parcel dialog
|
||||
/// </summary>
|
||||
public byte[] Bitmap {
|
||||
get {
|
||||
public byte[] Bitmap
|
||||
{
|
||||
get
|
||||
{
|
||||
return _bitmap;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_bitmap = value;
|
||||
}
|
||||
}
|
||||
|
@ -311,11 +379,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Parcel Description
|
||||
/// </summary>
|
||||
public string Description {
|
||||
get {
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
return _description;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_description = value;
|
||||
}
|
||||
}
|
||||
|
@ -323,11 +394,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Parcel settings. Access flags, Fly, NoPush, Voice, Scripts allowed, etc. ParcelFlags
|
||||
/// </summary>
|
||||
public uint Flags {
|
||||
get {
|
||||
public uint Flags
|
||||
{
|
||||
get
|
||||
{
|
||||
return _flags;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_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
|
||||
/// get constrainted to a specific point on teleport within the parcel
|
||||
/// </summary>
|
||||
public byte LandingType {
|
||||
get {
|
||||
public byte LandingType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _landingType;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_landingType = value;
|
||||
}
|
||||
}
|
||||
|
@ -348,11 +425,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Parcel Name
|
||||
/// </summary>
|
||||
public string Name {
|
||||
get {
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_name = value;
|
||||
}
|
||||
}
|
||||
|
@ -360,11 +440,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Status of Parcel, Leased, Abandoned, For Sale
|
||||
/// </summary>
|
||||
public ParcelStatus Status {
|
||||
get {
|
||||
public ParcelStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
return _status;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_status = value;
|
||||
}
|
||||
}
|
||||
|
@ -372,11 +455,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Internal ID of the parcel. Sometimes the client will try to use this value
|
||||
/// </summary>
|
||||
public int LocalID {
|
||||
get {
|
||||
public int LocalID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _localID;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_localID = value;
|
||||
}
|
||||
}
|
||||
|
@ -384,11 +470,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Determines if we scale the media based on the surface it's on
|
||||
/// </summary>
|
||||
public byte MediaAutoScale {
|
||||
get {
|
||||
public byte MediaAutoScale
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mediaAutoScale;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_mediaAutoScale = value;
|
||||
}
|
||||
}
|
||||
|
@ -396,11 +485,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Texture Guid to replace with the output of the media stream
|
||||
/// </summary>
|
||||
public UUID MediaID {
|
||||
get {
|
||||
public UUID MediaID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mediaID;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_mediaID = value;
|
||||
}
|
||||
}
|
||||
|
@ -408,11 +500,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// URL to the media file to display
|
||||
/// </summary>
|
||||
public string MediaURL {
|
||||
get {
|
||||
public string MediaURL
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mediaURL;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_mediaURL = value;
|
||||
}
|
||||
}
|
||||
|
@ -432,11 +527,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// URL to the shoutcast music stream to play on the parcel
|
||||
/// </summary>
|
||||
public string MusicURL {
|
||||
get {
|
||||
public string MusicURL
|
||||
{
|
||||
get
|
||||
{
|
||||
return _musicURL;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_musicURL = value;
|
||||
}
|
||||
}
|
||||
|
@ -445,11 +543,14 @@ namespace OpenSim.Framework
|
|||
/// Owner Avatar or Group of the parcel. Naturally, all land masses must be
|
||||
/// owned by someone
|
||||
/// </summary>
|
||||
public UUID OwnerID {
|
||||
get {
|
||||
public UUID OwnerID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ownerID;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_ownerID = value;
|
||||
}
|
||||
}
|
||||
|
@ -457,11 +558,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// List of access data for the parcel. User data, some bitflags, and a time
|
||||
/// </summary>
|
||||
public List<LandAccessEntry> ParcelAccessList {
|
||||
get {
|
||||
public List<LandAccessEntry> ParcelAccessList
|
||||
{
|
||||
get
|
||||
{
|
||||
return _parcelAccessList;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_parcelAccessList = value;
|
||||
}
|
||||
}
|
||||
|
@ -469,11 +573,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// How long in hours a Pass to the parcel is given
|
||||
/// </summary>
|
||||
public float PassHours {
|
||||
get {
|
||||
public float PassHours
|
||||
{
|
||||
get
|
||||
{
|
||||
return _passHours;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_passHours = value;
|
||||
}
|
||||
}
|
||||
|
@ -481,11 +588,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// Price to purchase a Pass to a restricted parcel
|
||||
/// </summary>
|
||||
public int PassPrice {
|
||||
get {
|
||||
public int PassPrice
|
||||
{
|
||||
get
|
||||
{
|
||||
return _passPrice;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_passPrice = value;
|
||||
}
|
||||
}
|
||||
|
@ -493,11 +603,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// When the parcel is being sold, this is the price to purchase the parcel
|
||||
/// </summary>
|
||||
public int SalePrice {
|
||||
get {
|
||||
public int SalePrice
|
||||
{
|
||||
get
|
||||
{
|
||||
return _salePrice;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_salePrice = value;
|
||||
}
|
||||
}
|
||||
|
@ -506,11 +619,14 @@ namespace OpenSim.Framework
|
|||
/// Number of meters^2 in the Simulator
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public int SimwideArea {
|
||||
get {
|
||||
public int SimwideArea
|
||||
{
|
||||
get
|
||||
{
|
||||
return _simwideArea;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_simwideArea = value;
|
||||
}
|
||||
}
|
||||
|
@ -519,11 +635,14 @@ namespace OpenSim.Framework
|
|||
/// Number of SceneObjectPart in the Simulator
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public int SimwidePrims {
|
||||
get {
|
||||
public int SimwidePrims
|
||||
{
|
||||
get
|
||||
{
|
||||
return _simwidePrims;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_simwidePrims = value;
|
||||
}
|
||||
}
|
||||
|
@ -531,11 +650,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// ID of the snapshot used in the client parcel dialog of the parcel
|
||||
/// </summary>
|
||||
public UUID SnapshotID {
|
||||
get {
|
||||
public UUID SnapshotID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _snapshotID;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_snapshotID = value;
|
||||
}
|
||||
}
|
||||
|
@ -544,11 +666,14 @@ namespace OpenSim.Framework
|
|||
/// When teleporting is restricted to a certain point, this is the location
|
||||
/// that the user will be redirected to
|
||||
/// </summary>
|
||||
public Vector3 UserLocation {
|
||||
get {
|
||||
public Vector3 UserLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userLocation;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_userLocation = value;
|
||||
}
|
||||
}
|
||||
|
@ -557,11 +682,14 @@ namespace OpenSim.Framework
|
|||
/// When teleporting is restricted to a certain point, this is the rotation
|
||||
/// that the user will be positioned
|
||||
/// </summary>
|
||||
public Vector3 UserLookAt {
|
||||
get {
|
||||
public Vector3 UserLookAt
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userLookAt;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_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
|
||||
/// the parcel and isn't set to the same 'group' as the parcel.
|
||||
/// </summary>
|
||||
public int OtherCleanTime {
|
||||
get {
|
||||
public int OtherCleanTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return _otherCleanTime;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_otherCleanTime = value;
|
||||
}
|
||||
}
|
||||
|
@ -582,11 +713,14 @@ namespace OpenSim.Framework
|
|||
/// <summary>
|
||||
/// parcel media description
|
||||
/// </summary>
|
||||
public string MediaDescription {
|
||||
get {
|
||||
public string MediaDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mediaDescription;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_mediaDescription = value;
|
||||
}
|
||||
}
|
||||
|
@ -622,7 +756,7 @@ namespace OpenSim.Framework
|
|||
landData._mediaURL = _mediaURL;
|
||||
landData._musicURL = _musicURL;
|
||||
landData._ownerID = _ownerID;
|
||||
landData._bitmap = (byte[]) _bitmap.Clone();
|
||||
landData._bitmap = (byte[])_bitmap.Clone();
|
||||
landData._description = _description;
|
||||
landData._flags = _flags;
|
||||
landData._name = _name;
|
||||
|
@ -643,6 +777,7 @@ namespace OpenSim.Framework
|
|||
landData._obscureMedia = _obscureMedia;
|
||||
landData._simwideArea = _simwideArea;
|
||||
landData._simwidePrims = _simwidePrims;
|
||||
landData._dwell = _dwell;
|
||||
|
||||
landData._parcelAccessList.Clear();
|
||||
foreach (LandAccessEntry entry in _parcelAccessList)
|
||||
|
|
|
@ -49,7 +49,11 @@ namespace OpenSim.Framework.Monitoring
|
|||
Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
|
||||
|
||||
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));
|
||||
|
||||
sb.AppendFormat(
|
||||
|
|
|
@ -60,13 +60,21 @@ namespace OpenSim.Framework.Monitoring
|
|||
private static bool m_enabled;
|
||||
|
||||
/// <summary>
|
||||
/// Average memory churn in bytes per millisecond.
|
||||
/// Last memory churn in bytes per millisecond.
|
||||
/// </summary>
|
||||
public static double AverageMemoryChurn
|
||||
{
|
||||
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>
|
||||
/// Maximum number of statistical samples.
|
||||
/// </summary>
|
||||
|
|
|
@ -359,13 +359,19 @@ Asset service request failures: {3}" + Environment.NewLine,
|
|||
inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime,
|
||||
netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime));
|
||||
|
||||
foreach (KeyValuePair<string, Stat> kvp in StatsManager.RegisteredStats)
|
||||
{
|
||||
Stat stat = kvp.Value;
|
||||
Dictionary<string, Dictionary<string, Stat>> sceneStats;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,13 +35,23 @@ namespace OpenSim.Framework.Monitoring
|
|||
/// </summary>
|
||||
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>
|
||||
/// Registered stats.
|
||||
/// Registered stats categorized by category/container/shortname
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Do not add or remove from this dictionary.
|
||||
/// Do not add or remove directly from this dictionary.
|
||||
/// </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 UserStatsCollector userStats;
|
||||
|
@ -51,6 +61,75 @@ namespace OpenSim.Framework.Monitoring
|
|||
public static UserStatsCollector UserStats { get { return userStats; } }
|
||||
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>
|
||||
/// Start collecting statistics related to assets.
|
||||
/// Should only be called once.
|
||||
|
@ -73,43 +152,100 @@ namespace OpenSim.Framework.Monitoring
|
|||
return userStats;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a statistic.
|
||||
/// </summary>
|
||||
/// <param name='stat'></param>
|
||||
/// <returns></returns>
|
||||
public static bool RegisterStat(Stat stat)
|
||||
{
|
||||
Dictionary<string, Dictionary<string, Stat>> category = null, newCategory;
|
||||
Dictionary<string, Stat> container = null, newContainer;
|
||||
|
||||
lock (RegisteredStats)
|
||||
{
|
||||
if (RegisteredStats.ContainsKey(stat.UniqueName))
|
||||
{
|
||||
// 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.
|
||||
// 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
|
||||
// in a class are run in the same instance of the VM.
|
||||
if (TryGetStat(stat, out category, out container))
|
||||
return false;
|
||||
|
||||
// throw new Exception(
|
||||
// "StatsManager already contains stat with ShortName {0} in Category {1}", stat.ShortName, stat.Category);
|
||||
}
|
||||
// We take a copy-on-write approach here of replacing dictionaries when keys are added or removed.
|
||||
// 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
|
||||
Dictionary<string, Stat> newRegisteredStats = new Dictionary<string, Stat>(RegisteredStats);
|
||||
newRegisteredStats[stat.UniqueName] = stat;
|
||||
RegisteredStats = newRegisteredStats;
|
||||
if (category != null)
|
||||
newCategory = new Dictionary<string, Dictionary<string, Stat>>(category);
|
||||
else
|
||||
newCategory = new Dictionary<string, Dictionary<string, Stat>>();
|
||||
|
||||
newContainer[stat.ShortName] = stat;
|
||||
newCategory[stat.Container] = newContainer;
|
||||
RegisteredStats[stat.Category] = newCategory;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deregister a statistic
|
||||
/// </summary>>
|
||||
/// <param name='stat'></param>
|
||||
/// <returns></returns
|
||||
public static bool DeregisterStat(Stat stat)
|
||||
{
|
||||
Dictionary<string, Dictionary<string, Stat>> category = null, newCategory;
|
||||
Dictionary<string, Stat> container = null, newContainer;
|
||||
|
||||
lock (RegisteredStats)
|
||||
{
|
||||
if (!RegisteredStats.ContainsKey(stat.UniqueName))
|
||||
if (!TryGetStat(stat, out category, out container))
|
||||
return false;
|
||||
|
||||
Dictionary<string, Stat> newRegisteredStats = new Dictionary<string, Stat>(RegisteredStats);
|
||||
newRegisteredStats.Remove(stat.UniqueName);
|
||||
RegisteredStats = newRegisteredStats;
|
||||
newContainer = new Dictionary<string, Stat>(container);
|
||||
newContainer.Remove(stat.UniqueName);
|
||||
|
||||
newCategory = new Dictionary<string, Dictionary<string, Stat>>(category);
|
||||
newCategory.Remove(stat.Container);
|
||||
|
||||
newCategory[stat.Container] = newContainer;
|
||||
RegisteredStats[stat.Category] = newCategory;
|
||||
|
||||
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>
|
||||
|
@ -157,9 +293,26 @@ namespace OpenSim.Framework.Monitoring
|
|||
|
||||
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(
|
||||
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;
|
||||
Name = name;
|
||||
UnitName = unitName;
|
||||
|
@ -175,6 +328,12 @@ namespace OpenSim.Framework.Monitoring
|
|||
{
|
||||
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
|
||||
|
@ -192,7 +351,7 @@ namespace OpenSim.Framework.Monitoring
|
|||
if (c == 0)
|
||||
return 0;
|
||||
|
||||
return (double)Antecedent / c;
|
||||
return (double)Antecedent / c * 100;
|
||||
}
|
||||
|
||||
set
|
||||
|
@ -203,8 +362,13 @@ namespace OpenSim.Framework.Monitoring
|
|||
|
||||
public PercentageStat(
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -231,7 +231,25 @@ namespace OpenSim.Framework.Monitoring
|
|||
private static bool RemoveThread(int threadID)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -96,11 +96,6 @@ namespace OpenSim.Framework.Servers
|
|||
get { return m_httpServer; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the non-viewer statistics collection object for this service/server
|
||||
/// </summary>
|
||||
protected IStatsCollector m_stats;
|
||||
|
||||
public BaseOpenSimServer()
|
||||
{
|
||||
m_startuptime = DateTime.Now;
|
||||
|
@ -177,10 +172,6 @@ namespace OpenSim.Framework.Servers
|
|||
"show info",
|
||||
"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",
|
||||
"show threads",
|
||||
"Show thread status", HandleShow);
|
||||
|
@ -226,12 +217,7 @@ namespace OpenSim.Framework.Servers
|
|||
{
|
||||
StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n");
|
||||
sb.Append(GetUptimeReport());
|
||||
|
||||
if (m_stats != null)
|
||||
{
|
||||
sb.Append(m_stats.Report());
|
||||
}
|
||||
|
||||
sb.Append(StatsManager.SimExtraStats.Report());
|
||||
sb.Append(Environment.NewLine);
|
||||
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("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 uptime - show server startup time and uptime.");
|
||||
Notice("show version - show server version.");
|
||||
|
@ -409,11 +391,6 @@ namespace OpenSim.Framework.Servers
|
|||
ShowInfo();
|
||||
break;
|
||||
|
||||
case "stats":
|
||||
if (m_stats != null)
|
||||
Notice(m_stats.Report());
|
||||
break;
|
||||
|
||||
case "threads":
|
||||
Notice(GetThreadsReport());
|
||||
break;
|
||||
|
@ -604,8 +581,7 @@ namespace OpenSim.Framework.Servers
|
|||
|
||||
public string osSecret {
|
||||
// Secret uuid for the simulator
|
||||
get { return m_osSecret; }
|
||||
|
||||
get { return m_osSecret; }
|
||||
}
|
||||
|
||||
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 (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
|
||||
{
|
||||
return m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
|
||||
return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -648,7 +648,7 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
// Every month or so this will wrap and give bad numbers, not really a problem
|
||||
// since its just for reporting
|
||||
int tickdiff = requestEndTick - requestStartTick;
|
||||
if (tickdiff > 3000 && requestHandler.Name != "GetTexture")
|
||||
if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture")
|
||||
{
|
||||
m_log.InfoFormat(
|
||||
"[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms",
|
||||
|
|
|
@ -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 scene",
|
||||
"debug scene <scripting> <collisions> <physics>",
|
||||
"Turn on scene debugging", Debug);
|
||||
"debug scene active|collisions|physics|scripting|teleport true|false",
|
||||
"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",
|
||||
"change region <region name>",
|
||||
|
@ -930,7 +936,8 @@ namespace OpenSim
|
|||
}
|
||||
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;
|
||||
|
|
|
@ -223,8 +223,6 @@ namespace OpenSim
|
|||
|
||||
base.StartupSpecific();
|
||||
|
||||
m_stats = StatsManager.SimExtraStats;
|
||||
|
||||
// Create a ModuleLoader instance
|
||||
m_moduleLoader = new ModuleLoader(m_config.Source);
|
||||
|
||||
|
@ -234,51 +232,51 @@ namespace OpenSim
|
|||
plugin.PostInitialise();
|
||||
}
|
||||
|
||||
AddPluginCommands();
|
||||
}
|
||||
|
||||
protected virtual void AddPluginCommands()
|
||||
{
|
||||
// If console exists add plugin commands.
|
||||
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
|
||||
// away at some point.
|
||||
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);
|
||||
if (commander == null)
|
||||
continue;
|
||||
|
||||
ICommander commander = null;
|
||||
|
||||
Scene s = SceneManager.CurrentOrFirstScene;
|
||||
|
||||
if (s != null && s.GetCommanders() != null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
foreach (string command in commander.Commands.Keys)
|
||||
{
|
||||
console.Commands.AddCommand(capitalizedTopic, false,
|
||||
topic + " " + command,
|
||||
topic + " " + commander.Commands[command].ShortHelp(),
|
||||
String.Empty, HandleCommanderCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,8 +163,8 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
m_HostCapsObj.RegisterHandler(
|
||||
"SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest, "SEED", null));
|
||||
|
||||
m_log.DebugFormat(
|
||||
"[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID);
|
||||
// m_log.DebugFormat(
|
||||
// "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID);
|
||||
|
||||
//m_capsHandlers["MapLayer"] =
|
||||
// new LLSDStreamhandler<OSDMapRequest, OSDMapLayerResponse>("POST",
|
||||
|
@ -254,11 +254,12 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
public string SeedCapRequest(string request, string path, string param,
|
||||
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))
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
m_log.WarnFormat(
|
||||
"[CAPS]: Unauthorized CAPS client {0} from {1}",
|
||||
m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint);
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
|
||||
//scene.CommsManager.HttpServer.AddLLSDHandler("/CAPS/EQG/", EventQueueFallBack);
|
||||
|
||||
scene.EventManager.OnNewClient += OnNewClient;
|
||||
// scene.EventManager.OnNewClient += OnNewClient;
|
||||
|
||||
// TODO: Leaving these open, or closing them when we
|
||||
// become a child is incorrect. It messes up TP in a big
|
||||
|
@ -102,6 +102,7 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
// circuit is there.
|
||||
|
||||
scene.EventManager.OnClientClosed += ClientClosed;
|
||||
|
||||
scene.EventManager.OnMakeChildAgent += MakeChildAgent;
|
||||
scene.EventManager.OnRegisterCaps += OnRegisterCaps;
|
||||
|
||||
|
@ -110,10 +111,10 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
false,
|
||||
"debug eq",
|
||||
"debug eq [0|1|2]",
|
||||
"Turn on event queue debugging"
|
||||
+ "<= 0 - turns off all event queue logging"
|
||||
+ ">= 1 - turns on outgoing event logging"
|
||||
+ ">= 2 - turns on poll notification",
|
||||
"Turn on event queue debugging\n"
|
||||
+ " <= 0 - turns off all event queue logging\n"
|
||||
+ " >= 1 - turns on outgoing event logging\n"
|
||||
+ " >= 2 - turns on poll notification",
|
||||
HandleDebugEq);
|
||||
}
|
||||
else
|
||||
|
@ -226,16 +227,6 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
|
||||
#endregion
|
||||
|
||||
private void OnNewClient(IClientAPI client)
|
||||
{
|
||||
//client.OnLogout += ClientClosed;
|
||||
}
|
||||
|
||||
// private void ClientClosed(IClientAPI client)
|
||||
// {
|
||||
// ClientClosed(client.AgentId);
|
||||
// }
|
||||
|
||||
private void ClientClosed(UUID agentID, Scene scene)
|
||||
{
|
||||
// m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName);
|
||||
|
|
|
@ -107,7 +107,7 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
|
||||
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(
|
||||
"SimConsoleAsync",
|
||||
new ConsoleHandler("/CAPS/" + capID + "/", "SimConsoleAsync", agentID, this, m_scene));
|
||||
|
|
|
@ -45,7 +45,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
public Packet Packet;
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// No arg constructor.
|
||||
/// </summary>
|
||||
public IncomingPacket() {}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="client">Reference to the client this packet came from</param>
|
||||
/// <param name="packet">Packet data</param>
|
||||
|
|
|
@ -347,7 +347,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
private int m_moneyBalance;
|
||||
private int m_animationSequenceNumber = 1;
|
||||
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<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;
|
||||
|
||||
ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
|
||||
ImprovedTerseObjectUpdatePacket packet
|
||||
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
|
||||
|
||||
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
|
||||
packet.RegionData.TimeDilation = timeDilation;
|
||||
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
|
||||
|
@ -3967,7 +3979,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
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.TimeDilation = timeDilation;
|
||||
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.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;
|
||||
|
||||
if (textureEntry != null && textureEntry.Length > 0)
|
||||
|
@ -5191,7 +5208,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
protected virtual void RegisterLocalPacketHandlers()
|
||||
{
|
||||
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.ViewerEffect, HandleViewerEffect, false);
|
||||
AddLocalPacketHandler(PacketType.AgentCachedTexture, HandleAgentTextureCached, false);
|
||||
AddLocalPacketHandler(PacketType.MultipleObjectUpdate, HandleMultipleObjUpdate, false);
|
||||
|
@ -5418,80 +5439,83 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
#region Scene/Avatar
|
||||
|
||||
private bool HandleAgentUpdate(IClientAPI sener, Packet Pack)
|
||||
private bool HandleAgentUpdate(IClientAPI sener, Packet packet)
|
||||
{
|
||||
if (OnAgentUpdate != null)
|
||||
{
|
||||
bool update = false;
|
||||
AgentUpdatePacket agenUpdate = (AgentUpdatePacket)Pack;
|
||||
AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet;
|
||||
|
||||
#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;
|
||||
}
|
||||
#endregion
|
||||
|
||||
AgentUpdatePacket.AgentDataBlock x = agenUpdate.AgentData;
|
||||
bool update = false;
|
||||
AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData;
|
||||
|
||||
// We can only check when we have something to check
|
||||
// against.
|
||||
|
||||
if (lastarg != null)
|
||||
if (m_lastAgentUpdateArgs != null)
|
||||
{
|
||||
// These should be ordered from most-likely to
|
||||
// least likely to change. I've made an initial
|
||||
// guess at that.
|
||||
update =
|
||||
(
|
||||
(x.BodyRotation != lastarg.BodyRotation) ||
|
||||
(x.CameraAtAxis != lastarg.CameraAtAxis) ||
|
||||
(x.CameraCenter != lastarg.CameraCenter) ||
|
||||
(x.CameraLeftAxis != lastarg.CameraLeftAxis) ||
|
||||
(x.CameraUpAxis != lastarg.CameraUpAxis) ||
|
||||
(x.ControlFlags != lastarg.ControlFlags) ||
|
||||
(x.Far != lastarg.Far) ||
|
||||
(x.Flags != lastarg.Flags) ||
|
||||
(x.State != lastarg.State) ||
|
||||
(x.HeadRotation != lastarg.HeadRotation) ||
|
||||
(x.SessionID != lastarg.SessionID) ||
|
||||
(x.AgentID != lastarg.AgentID)
|
||||
(x.BodyRotation != m_lastAgentUpdateArgs.BodyRotation) ||
|
||||
(x.CameraAtAxis != m_lastAgentUpdateArgs.CameraAtAxis) ||
|
||||
(x.CameraCenter != m_lastAgentUpdateArgs.CameraCenter) ||
|
||||
(x.CameraLeftAxis != m_lastAgentUpdateArgs.CameraLeftAxis) ||
|
||||
(x.CameraUpAxis != m_lastAgentUpdateArgs.CameraUpAxis) ||
|
||||
(x.ControlFlags != m_lastAgentUpdateArgs.ControlFlags) ||
|
||||
(x.Far != m_lastAgentUpdateArgs.Far) ||
|
||||
(x.Flags != m_lastAgentUpdateArgs.Flags) ||
|
||||
(x.State != m_lastAgentUpdateArgs.State) ||
|
||||
(x.HeadRotation != m_lastAgentUpdateArgs.HeadRotation) ||
|
||||
(x.SessionID != m_lastAgentUpdateArgs.SessionID) ||
|
||||
(x.AgentID != m_lastAgentUpdateArgs.AgentID)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lastAgentUpdateArgs = new AgentUpdateArgs();
|
||||
update = true;
|
||||
}
|
||||
|
||||
// These should be ordered from most-likely to
|
||||
// least likely to change. I've made an initial
|
||||
// guess at that.
|
||||
|
||||
if (update)
|
||||
{
|
||||
// m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name);
|
||||
|
||||
AgentUpdateArgs arg = new AgentUpdateArgs();
|
||||
arg.AgentID = x.AgentID;
|
||||
arg.BodyRotation = x.BodyRotation;
|
||||
arg.CameraAtAxis = x.CameraAtAxis;
|
||||
arg.CameraCenter = x.CameraCenter;
|
||||
arg.CameraLeftAxis = x.CameraLeftAxis;
|
||||
arg.CameraUpAxis = x.CameraUpAxis;
|
||||
arg.ControlFlags = x.ControlFlags;
|
||||
arg.Far = x.Far;
|
||||
arg.Flags = x.Flags;
|
||||
arg.HeadRotation = x.HeadRotation;
|
||||
arg.SessionID = x.SessionID;
|
||||
arg.State = x.State;
|
||||
m_lastAgentUpdateArgs.AgentID = x.AgentID;
|
||||
m_lastAgentUpdateArgs.BodyRotation = x.BodyRotation;
|
||||
m_lastAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis;
|
||||
m_lastAgentUpdateArgs.CameraCenter = x.CameraCenter;
|
||||
m_lastAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis;
|
||||
m_lastAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis;
|
||||
m_lastAgentUpdateArgs.ControlFlags = x.ControlFlags;
|
||||
m_lastAgentUpdateArgs.Far = x.Far;
|
||||
m_lastAgentUpdateArgs.Flags = x.Flags;
|
||||
m_lastAgentUpdateArgs.HeadRotation = x.HeadRotation;
|
||||
m_lastAgentUpdateArgs.SessionID = x.SessionID;
|
||||
m_lastAgentUpdateArgs.State = x.State;
|
||||
|
||||
UpdateAgent handlerAgentUpdate = OnAgentUpdate;
|
||||
UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate;
|
||||
lastarg = arg; // save this set of arguments for nexttime
|
||||
|
||||
if (handlerPreAgentUpdate != null)
|
||||
OnPreAgentUpdate(this, arg);
|
||||
OnPreAgentUpdate(this, m_lastAgentUpdateArgs);
|
||||
|
||||
if (handlerAgentUpdate != null)
|
||||
OnAgentUpdate(this, arg);
|
||||
OnAgentUpdate(this, m_lastAgentUpdateArgs);
|
||||
|
||||
handlerAgentUpdate = null;
|
||||
handlerPreAgentUpdate = null;
|
||||
}
|
||||
}
|
||||
|
||||
PacketPool.Instance.ReturnPacket(packet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -9056,7 +9080,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
}
|
||||
#endregion
|
||||
|
||||
switch (Utils.BytesToString(messagePacket.MethodData.Method))
|
||||
string method = Utils.BytesToString(messagePacket.MethodData.Method);
|
||||
|
||||
switch (method)
|
||||
{
|
||||
case "getinfo":
|
||||
if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false))
|
||||
|
@ -9372,7 +9398,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
return true;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -11758,7 +11794,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
logPacket = false;
|
||||
|
||||
if (DebugPacketLevel <= 50
|
||||
& (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate))
|
||||
&& (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate))
|
||||
logPacket = false;
|
||||
|
||||
if (DebugPacketLevel <= 25 && packet.Type == PacketType.ObjectPropertiesFamily)
|
||||
|
@ -11832,8 +11868,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
if (!ProcessPacketMethod(packet))
|
||||
m_log.Warn("[CLIENT]: unhandled packet " + packet.Type);
|
||||
|
||||
PacketPool.Instance.ReturnPacket(packet);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
|
||||
ImprovedTerseObjectUpdatePacket packet
|
||||
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
|
||||
PacketType.ImprovedTerseObjectUpdate);
|
||||
|
||||
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
|
||||
packet.RegionData.TimeDilation = timeDilation;
|
||||
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
|
||||
|
|
|
@ -37,6 +37,7 @@ using log4net;
|
|||
using Nini.Config;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Console;
|
||||
using OpenSim.Framework.Monitoring;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenMetaverse;
|
||||
|
@ -100,9 +101,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
/// <summary>The measured resolution of Environment.TickCount</summary>
|
||||
public readonly float TickCountResolution;
|
||||
|
||||
/// <summary>Number of prim updates to put on the queue each time the
|
||||
/// OnQueueEmpty event is triggered for updates</summary>
|
||||
public readonly int PrimUpdatesPerCallback;
|
||||
|
||||
/// <summary>Number of texture packets to put on the queue each time the
|
||||
/// OnQueueEmpty event is triggered for textures</summary>
|
||||
public readonly int TextureSendLimit;
|
||||
|
@ -111,6 +114,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
//PacketEventDictionary packetEvents = new PacketEventDictionary();
|
||||
/// <summary>Incoming packets that are awaiting handling</summary>
|
||||
private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
|
||||
|
||||
/// <summary></summary>
|
||||
//private UDPClientCollection m_clients = new UDPClientCollection();
|
||||
/// <summary>Bandwidth throttle for this UDP server</summary>
|
||||
|
@ -121,28 +125,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
/// <summary>Manages authentication for agent circuits</summary>
|
||||
private AgentCircuitManager m_circuitManager;
|
||||
|
||||
/// <summary>Reference to the scene this UDP server is attached to</summary>
|
||||
protected Scene m_scene;
|
||||
|
||||
/// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary>
|
||||
private Location m_location;
|
||||
|
||||
/// <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
|
||||
/// stack. Use zero to leave this value as the default</summary>
|
||||
private int m_recvBufferSize;
|
||||
|
||||
/// <summary>Flag to process packets asynchronously or synchronously</summary>
|
||||
private bool m_asyncPacketHandling;
|
||||
|
||||
/// <summary>Tracks whether or not a packet was sent each round so we know
|
||||
/// whether or not to sleep</summary>
|
||||
private bool m_packetSent;
|
||||
|
||||
/// <summary>Environment.TickCount of the last time that packet stats were reported to the scene</summary>
|
||||
private int m_elapsedMSSinceLastStatReport = 0;
|
||||
|
||||
/// <summary>Environment.TickCount of the last time the outgoing packet handler executed</summary>
|
||||
private int m_tickLastOutgoingPacketHandler;
|
||||
|
||||
/// <summary>Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped</summary>
|
||||
private int m_elapsedMSOutgoingPacketHandler;
|
||||
|
||||
/// <summary>Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed</summary>
|
||||
private int m_elapsed100MSOutgoingPacketHandler;
|
||||
|
||||
/// <summary>Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed</summary>
|
||||
private int m_elapsed500MSOutgoingPacketHandler;
|
||||
|
||||
|
@ -155,6 +168,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// <summary>Flag to signal when clients should send pings</summary>
|
||||
protected bool m_sendPing;
|
||||
|
||||
private Pool<IncomingPacket> m_incomingPacketPool;
|
||||
|
||||
private int m_defaultRTO = 0;
|
||||
private int m_maxRTO = 0;
|
||||
private int m_ackTimeout = 0;
|
||||
|
@ -175,7 +190,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// </summary>
|
||||
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)
|
||||
{
|
||||
#region Environment.TickCount Measurement
|
||||
|
@ -229,6 +246,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true);
|
||||
PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true);
|
||||
UsePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", false);
|
||||
}
|
||||
|
||||
#region BinaryStats
|
||||
|
@ -258,20 +276,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
m_throttle = new TokenBucket(null, sceneThrottleBps);
|
||||
ThrottleRates = new ThrottleRates(configSource);
|
||||
|
||||
if (UsePools)
|
||||
m_incomingPacketPool = new Pool<IncomingPacket>(() => new IncomingPacket(), 500);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (m_scene == null)
|
||||
throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference");
|
||||
StartInbound();
|
||||
StartOutbound();
|
||||
|
||||
m_elapsedMSSinceLastStatReport = Environment.TickCount;
|
||||
}
|
||||
|
||||
private void StartInbound()
|
||||
{
|
||||
m_log.InfoFormat(
|
||||
"[LLUDPSERVER]: Starting the LLUDP server in {0} mode",
|
||||
m_asyncPacketHandling ? "asynchronous" : "synchronous");
|
||||
"[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
|
||||
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(
|
||||
IncomingPacketHandler,
|
||||
string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName),
|
||||
|
@ -280,6 +306,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
true,
|
||||
GetWatchdogIncomingAlarmData,
|
||||
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(
|
||||
OutgoingPacketHandler,
|
||||
|
@ -289,8 +322,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
true,
|
||||
GetWatchdogOutgoingAlarmData,
|
||||
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>
|
||||
|
@ -315,12 +353,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
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)
|
||||
{
|
||||
if (m_scene != null)
|
||||
|
@ -337,6 +369,81 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
m_scene = (Scene)scene;
|
||||
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)
|
||||
|
@ -420,6 +527,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
byte[] data = packet.ToBytes();
|
||||
SendPacketData(udpClient, data, packet.Type, category, method);
|
||||
}
|
||||
|
||||
PacketPool.Instance.ReturnPacket(packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -704,7 +813,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
LLUDPClient udpClient = null;
|
||||
Packet packet = null;
|
||||
int packetEnd = buffer.DataLength - 1;
|
||||
IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
|
||||
IPEndPoint endPoint = (IPEndPoint)buffer.RemoteEndPoint;
|
||||
|
||||
#region Decoding
|
||||
|
||||
|
@ -714,7 +823,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}",
|
||||
// buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
|
||||
|
||||
return; // Drop undersizd packet
|
||||
return; // Drop undersized packet
|
||||
}
|
||||
|
||||
int headerLen = 7;
|
||||
|
@ -737,7 +846,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
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
|
||||
((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
|
||||
}
|
||||
|
@ -752,11 +867,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
return; // Drop short packet
|
||||
}
|
||||
catch(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
if (m_malformedCount < 100)
|
||||
m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString());
|
||||
|
||||
m_malformedCount++;
|
||||
|
||||
if ((m_malformedCount % 100000) == 0)
|
||||
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
|
||||
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);
|
||||
|
||||
|
@ -786,7 +905,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
// Determine which agent this packet came from
|
||||
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);
|
||||
return;
|
||||
|
@ -810,6 +929,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// Handle appended ACKs
|
||||
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++)
|
||||
udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent);
|
||||
}
|
||||
|
@ -819,6 +942,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
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++)
|
||||
udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent);
|
||||
|
||||
|
@ -832,6 +959,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
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);
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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
|
||||
StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
|
||||
CompletePing(udpClient, startPing.PingID.PingID);
|
||||
|
@ -897,8 +1030,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
#endregion Ping Check Handling
|
||||
|
||||
IncomingPacket incomingPacket;
|
||||
|
||||
// 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
|
||||
|
@ -984,21 +1130,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
private void HandleUseCircuitCode(object o)
|
||||
{
|
||||
IPEndPoint remoteEndPoint = null;
|
||||
IPEndPoint endPoint = null;
|
||||
IClientAPI client = null;
|
||||
|
||||
try
|
||||
{
|
||||
// DateTime startTime = DateTime.Now;
|
||||
object[] array = (object[])o;
|
||||
UDPPacketBuffer buffer = (UDPPacketBuffer)array[0];
|
||||
endPoint = (IPEndPoint)array[0];
|
||||
UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1];
|
||||
|
||||
m_log.DebugFormat(
|
||||
"[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}",
|
||||
uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, buffer.RemoteEndPoint);
|
||||
|
||||
remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint;
|
||||
uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, endPoint);
|
||||
|
||||
AuthenticateResponse sessionInfo;
|
||||
if (IsClientAuthorized(uccp, out sessionInfo))
|
||||
|
@ -1009,13 +1153,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
uccp.CircuitCode.Code,
|
||||
uccp.CircuitCode.ID,
|
||||
uccp.CircuitCode.SessionID,
|
||||
remoteEndPoint,
|
||||
endPoint,
|
||||
sessionInfo);
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
if (client != null)
|
||||
|
@ -1026,7 +1170,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// Don't create clients for unauthorized requesters.
|
||||
m_log.WarnFormat(
|
||||
"[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(
|
||||
|
@ -1038,7 +1182,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
m_log.ErrorFormat(
|
||||
"[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.AgentId.ToString() : "unknown",
|
||||
e.Message,
|
||||
|
@ -1103,20 +1247,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
IClientAPI client = null;
|
||||
|
||||
// In priciple there shouldn't be more than one thread here, ever.
|
||||
// But in case that happens, we need to synchronize this piece of code
|
||||
// because it's too important
|
||||
lock (this)
|
||||
// We currently synchronize this code across the whole scene to avoid issues such as
|
||||
// http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done
|
||||
// consistently, this lock could probably be removed.
|
||||
lock (this)
|
||||
{
|
||||
if (!m_scene.TryGetClient(agentID, out client))
|
||||
{
|
||||
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.OnLogout += LogoutHandler;
|
||||
|
||||
|
||||
((LLClientView)client).DisableFacelights = m_disableFacelights;
|
||||
|
||||
|
||||
client.Start();
|
||||
}
|
||||
}
|
||||
|
@ -1155,7 +1299,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// on to en-US to avoid number parsing issues
|
||||
Culture.SetCurrentCulture();
|
||||
|
||||
while (base.IsRunning)
|
||||
while (IsRunningInbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -1170,7 +1314,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
}
|
||||
|
||||
if (packetInbox.Dequeue(100, ref incomingPacket))
|
||||
{
|
||||
ProcessInPacket(incomingPacket);//, incomingPacket); Util.FireAndForget(ProcessInPacket, incomingPacket);
|
||||
|
||||
if (UsePools)
|
||||
m_incomingPacketPool.ReturnObject(incomingPacket);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -1197,7 +1346,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// Action generic every round
|
||||
Action<IClientAPI> clientPacketHandler = ClientOutgoingPacketHandler;
|
||||
|
||||
while (base.IsRunning)
|
||||
while (base.IsRunningOutbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -30,6 +30,7 @@ using System.Net;
|
|||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using OpenSim.Framework;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
|
@ -58,17 +59,29 @@ namespace OpenMetaverse
|
|||
/// <summary>Flag to process packets asynchronously or synchronously</summary>
|
||||
private bool m_asyncPacketHandling;
|
||||
|
||||
/// <summary>The all important shutdown flag</summary>
|
||||
private volatile bool m_shutdownFlag = true;
|
||||
/// <summary>
|
||||
/// 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>
|
||||
public bool IsRunning { get { return !m_shutdownFlag; } }
|
||||
/// <summary>
|
||||
/// 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>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
/// <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="usePool">Are we to use an object pool to get objects for handing inbound data?</param>
|
||||
public OpenSimUDPBase(IPAddress bindAddress, int port)
|
||||
{
|
||||
m_localBindAddress = bindAddress;
|
||||
|
@ -76,7 +89,7 @@ namespace OpenMetaverse
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the UDP server
|
||||
/// Start inbound UDP packet handling.
|
||||
/// </summary>
|
||||
/// <param name="recvBufferSize">The size of the receive buffer for
|
||||
/// 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
|
||||
/// connection). This call is ignored on Mono where the flag is not
|
||||
/// 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;
|
||||
|
||||
if (m_shutdownFlag)
|
||||
if (!IsRunningInbound)
|
||||
{
|
||||
const int SIO_UDP_CONNRESET = -1744830452;
|
||||
|
||||
|
@ -127,8 +145,7 @@ namespace OpenMetaverse
|
|||
|
||||
m_udpSocket.Bind(ipep);
|
||||
|
||||
// we're not shutting down, we're starting up
|
||||
m_shutdownFlag = false;
|
||||
IsRunningInbound = true;
|
||||
|
||||
// kick off an async receive. The Start() method will return, the
|
||||
// actual receives will occur asynchronously and will be caught in
|
||||
|
@ -138,28 +155,41 @@ namespace OpenMetaverse
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the UDP server
|
||||
/// Start outbound UDP packet handling.
|
||||
/// </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
|
||||
// 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.
|
||||
m_shutdownFlag = true;
|
||||
IsRunningInbound = false;
|
||||
m_udpSocket.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void StopOutbound()
|
||||
{
|
||||
IsRunningOutbound = false;
|
||||
}
|
||||
|
||||
private void AsyncBeginReceive()
|
||||
{
|
||||
// allocate a packet buffer
|
||||
//WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
|
||||
UDPPacketBuffer buf = new UDPPacketBuffer();
|
||||
UDPPacketBuffer buf;
|
||||
|
||||
if (!m_shutdownFlag)
|
||||
if (UsePools)
|
||||
buf = m_pool.GetObject();
|
||||
else
|
||||
buf = new UDPPacketBuffer();
|
||||
|
||||
if (IsRunningInbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -212,7 +242,7 @@ namespace OpenMetaverse
|
|||
{
|
||||
// Asynchronous receive operations will complete here through the call
|
||||
// to AsyncBeginReceive
|
||||
if (!m_shutdownFlag)
|
||||
if (IsRunningInbound)
|
||||
{
|
||||
// Asynchronous mode will start another receive before the
|
||||
// callback for this packet is even fired. Very parallel :-)
|
||||
|
@ -221,8 +251,6 @@ namespace OpenMetaverse
|
|||
|
||||
// get the buffer that was created in AsyncBeginReceive
|
||||
// this is the received data
|
||||
//WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState;
|
||||
//UDPPacketBuffer buffer = wrappedBuffer.Instance;
|
||||
UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
|
||||
|
||||
try
|
||||
|
@ -239,7 +267,8 @@ namespace OpenMetaverse
|
|||
catch (ObjectDisposedException) { }
|
||||
finally
|
||||
{
|
||||
//wrappedBuffer.Dispose();
|
||||
if (UsePools)
|
||||
m_pool.ReturnObject(buffer);
|
||||
|
||||
// Synchronous mode waits until the packet callback completes
|
||||
// before starting the receive to fetch another packet
|
||||
|
@ -252,7 +281,7 @@ namespace OpenMetaverse
|
|||
|
||||
public void AsyncBeginSend(UDPPacketBuffer buf)
|
||||
{
|
||||
if (!m_shutdownFlag)
|
||||
if (IsRunningOutbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@ using System.Reflection;
|
|||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using log4net;
|
||||
using OpenSim.Framework.Monitoring;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
|
@ -43,17 +44,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
private bool packetPoolEnabled = 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>
|
||||
/// Pool of packets available for reuse.
|
||||
/// </summary>
|
||||
private readonly Dictionary<PacketType, Stack<Packet>> pool = new Dictionary<PacketType, Stack<Packet>>();
|
||||
|
||||
private static Dictionary<Type, Stack<Object>> DataBlocks =
|
||||
new Dictionary<Type, Stack<Object>>();
|
||||
|
||||
static PacketPool()
|
||||
{
|
||||
}
|
||||
private static Dictionary<Type, Stack<Object>> DataBlocks = new Dictionary<Type, Stack<Object>>();
|
||||
|
||||
public static PacketPool Instance
|
||||
{
|
||||
|
@ -72,8 +84,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
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)
|
||||
{
|
||||
m_packetsReusedStat.Consequent++;
|
||||
|
||||
Packet packet;
|
||||
|
||||
if (!packetPoolEnabled)
|
||||
|
@ -83,13 +108,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
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
|
||||
packet = Packet.BuildPacket(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
// m_log.DebugFormat("[PACKETPOOL]: Pulling {0} packet", type);
|
||||
|
||||
// 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);
|
||||
|
||||
Array.Clear(zeroBuffer, 0, zeroBuffer.Length);
|
||||
// Array.Clear(zeroBuffer, 0, zeroBuffer.Length);
|
||||
|
||||
int i = 0;
|
||||
Packet packet = GetPacket(type);
|
||||
|
@ -185,6 +216,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
switch (packet.Type)
|
||||
{
|
||||
// List pooling packets here
|
||||
case PacketType.AgentUpdate:
|
||||
case PacketType.PacketAck:
|
||||
case PacketType.ObjectUpdate:
|
||||
case PacketType.ImprovedTerseObjectUpdate:
|
||||
|
@ -199,7 +231,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
if ((pool[type]).Count < 50)
|
||||
{
|
||||
(pool[type]).Push(packet);
|
||||
// m_log.DebugFormat("[PACKETPOOL]: Pushing {0} packet", type);
|
||||
|
||||
pool[type].Push(packet);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
m_blocksReusedStat.Consequent++;
|
||||
|
||||
Stack<Object> s;
|
||||
|
||||
if (DataBlocks.TryGetValue(typeof(T), out s))
|
||||
{
|
||||
if (s.Count > 0)
|
||||
{
|
||||
m_blocksReusedStat.Antecedent++;
|
||||
return (T)s.Pop();
|
||||
}
|
||||
}
|
||||
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)
|
||||
return;
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
|||
/// This will contain basic tests for the LindenUDP client stack
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class BasicCircuitTests
|
||||
public class BasicCircuitTests : OpenSimTestCase
|
||||
{
|
||||
private Scene m_scene;
|
||||
private TestLLUDPServer m_udpServer;
|
||||
|
@ -65,8 +65,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
|||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
public override void SetUp()
|
||||
{
|
||||
base.SetUp();
|
||||
m_scene = new SceneHelpers().SetupScene();
|
||||
}
|
||||
|
||||
|
@ -143,7 +144,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
|||
public void TestAddClient()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// XmlConfigurator.Configure();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
AddUdpServer();
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
|
@ -495,42 +496,36 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
|
|||
|
||||
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;
|
||||
if (UUID.TryParse(friend.Friend, out friendID))
|
||||
UUID friendUuid;
|
||||
if (UUID.TryParse(friendStringId, out friendUuid))
|
||||
{
|
||||
// Try local
|
||||
if (LocalStatusNotification(userID, friendID, online))
|
||||
if (LocalStatusNotification(userID, friendUuid, online))
|
||||
continue;
|
||||
|
||||
// The friend is not here [as root]. Let's forward.
|
||||
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.
|
||||
remoteFriendStringIds.Add(friendStringId);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -486,6 +486,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
if (sp.ParentID != (uint)0)
|
||||
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);
|
||||
|
||||
// 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
|
||||
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);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
|
|||
{
|
||||
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;
|
||||
|
||||
|
|
|
@ -290,7 +290,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
|||
|
||||
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)
|
||||
{
|
||||
|
@ -324,7 +324,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
|||
Util.FireAndForget(delegate(object o)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -207,6 +207,7 @@ namespace OpenSim.Region.CoreModules.World.Land
|
|||
client.OnParcelInfoRequest += ClientOnParcelInfoRequest;
|
||||
client.OnParcelDeedToGroup += ClientOnParcelDeedToGroup;
|
||||
client.OnPreAgentUpdate += ClientOnPreAgentUpdate;
|
||||
client.OnParcelDwellRequest += ClientOnParcelDwellRequest;
|
||||
|
||||
EntityBase presenceEntity;
|
||||
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
|
||||
|
||||
#region Parcel Modification
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
@ -83,53 +84,87 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
m_console.Commands.AddCommand(
|
||||
"Objects", false, "delete object owner",
|
||||
"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(
|
||||
"Objects", false, "delete object creator",
|
||||
"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(
|
||||
"Objects", false, "delete object uuid",
|
||||
"delete object uuid <UUID>",
|
||||
"Delete a scene object by uuid", HandleDeleteObject);
|
||||
"Delete a scene object by uuid",
|
||||
HandleDeleteObject);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Objects", false, "delete object name",
|
||||
"delete object name [--regex] <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);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Objects", false, "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(
|
||||
"Objects",
|
||||
false,
|
||||
"show object uuid",
|
||||
"show object uuid <UUID>",
|
||||
"Show details of a scene object with the given UUID", HandleShowObjectByUuid);
|
||||
"show object uuid [--full] <UUID>",
|
||||
"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(
|
||||
"Objects",
|
||||
false,
|
||||
"show object name",
|
||||
"show object name [--regex] <name>",
|
||||
"show object name [--full] [--regex] <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);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Objects",
|
||||
false,
|
||||
"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.",
|
||||
"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 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"
|
||||
|
@ -188,7 +223,12 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
// 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);
|
||||
|
||||
|
@ -196,7 +236,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
|
||||
foreach (SceneObjectGroup so in sceneObjects)
|
||||
{
|
||||
AddSceneObjectReport(sb, so);
|
||||
AddSceneObjectReport(sb, so, showFull);
|
||||
sb.Append("\n");
|
||||
}
|
||||
|
||||
|
@ -205,7 +245,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
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<SceneObjectPart> parts = new List<SceneObjectPart>();
|
||||
|
@ -216,7 +256,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
|
||||
foreach (SceneObjectPart part in parts)
|
||||
{
|
||||
AddScenePartReport(sb, part);
|
||||
AddScenePartReport(sb, part, showFull);
|
||||
sb.Append("\n");
|
||||
}
|
||||
|
||||
|
@ -225,21 +265,26 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
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))
|
||||
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>");
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -252,7 +297,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
AddSceneObjectReport(sb, so);
|
||||
AddSceneObjectReport(sb, so, showFull);
|
||||
|
||||
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))
|
||||
return;
|
||||
|
||||
bool showFull = 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);
|
||||
|
||||
if (mainParams.Count < 4)
|
||||
{
|
||||
m_console.OutputFormat("Usage: show object name [--regex] <name>");
|
||||
m_console.OutputFormat("Usage: show object name [--full] [--regex] <name>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -287,7 +335,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
searchPredicate = so => so.Name == name;
|
||||
}
|
||||
|
||||
OutputSogsToConsole(searchPredicate);
|
||||
OutputSogsToConsole(searchPredicate, showFull);
|
||||
}
|
||||
|
||||
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))
|
||||
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;
|
||||
}
|
||||
|
||||
string rawConsoleStartVector = cmdparams[3];
|
||||
Vector3 startVector;
|
||||
Vector3 startVector, endVector;
|
||||
|
||||
if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector))
|
||||
{
|
||||
m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector);
|
||||
if (!TryParseVectorRange(cmdparams.Skip(3).Take(3), out startVector, out endVector))
|
||||
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
|
||||
= 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))
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -352,7 +398,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
AddScenePartReport(sb, sop);
|
||||
AddScenePartReport(sb, sop, true);
|
||||
|
||||
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))
|
||||
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;
|
||||
}
|
||||
|
||||
string rawConsoleStartVector = cmdparams[3];
|
||||
string rawConsoleStartVector = mainParams[3];
|
||||
Vector3 startVector;
|
||||
|
||||
if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector))
|
||||
|
@ -377,7 +429,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
return;
|
||||
}
|
||||
|
||||
string rawConsoleEndVector = cmdparams[5];
|
||||
string rawConsoleEndVector = mainParams[5];
|
||||
Vector3 endVector;
|
||||
|
||||
if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector))
|
||||
|
@ -386,7 +438,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
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)
|
||||
|
@ -394,14 +446,17 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
|
||||
return;
|
||||
|
||||
// bool showFull = 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);
|
||||
|
||||
if (mainParams.Count < 4)
|
||||
{
|
||||
m_console.OutputFormat("Usage: show part name [--regex] <name>");
|
||||
m_console.OutputFormat("Usage: show part name [--full] [--regex] <name>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -419,31 +474,112 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
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);
|
||||
sb.AppendFormat("Description: {0}\n", so.Description);
|
||||
sb.AppendFormat("Location: {0} @ {1}\n", so.AbsolutePosition, so.Scene.RegionInfo.RegionName);
|
||||
sb.AppendFormat("Parts: {0}\n", so.PrimCount);
|
||||
sb.AppendFormat("Flags: {0}\n", so.RootPart.Flags);
|
||||
if (showFull)
|
||||
{
|
||||
foreach (SceneObjectPart sop in so.Parts)
|
||||
{
|
||||
AddScenePartReport(sb, sop, false);
|
||||
sb.Append("\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddSummarySceneObjectReport(sb, so);
|
||||
}
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
private StringBuilder AddScenePartReport(StringBuilder sb, SceneObjectPart sop)
|
||||
private StringBuilder AddSummarySceneObjectReport(StringBuilder sb, SceneObjectGroup so)
|
||||
{
|
||||
sb.AppendFormat("Name: {0}\n", sop.Name);
|
||||
sb.AppendFormat("Description: {0}\n", sop.Description);
|
||||
sb.AppendFormat("Location: {0} @ {1}\n", sop.AbsolutePosition, sop.ParentGroup.Scene.RegionInfo.RegionName);
|
||||
sb.AppendFormat("Parent: {0}",
|
||||
sop.IsRoot ? "Is Root\n" : string.Format("{0} {1}\n", sop.ParentGroup.Name, sop.ParentGroup.UUID));
|
||||
sb.AppendFormat("Link number: {0}\n", sop.LinkNum);
|
||||
sb.AppendFormat("Flags: {0}\n", sop.Flags);
|
||||
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
||||
cdl.AddRow("Name", so.Name);
|
||||
cdl.AddRow("Descrition", so.Description);
|
||||
cdl.AddRow("Local ID", so.LocalId);
|
||||
cdl.AddRow("UUID", so.UUID);
|
||||
cdl.AddRow("Location", string.Format("{0} @ {1}", so.AbsolutePosition, so.Scene.Name));
|
||||
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)
|
||||
|
@ -557,6 +693,10 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
|
||||
break;
|
||||
|
||||
case "pos":
|
||||
deletes = GetDeleteCandidatesByPos(module, cmd);
|
||||
break;
|
||||
|
||||
default:
|
||||
m_console.OutputFormat("Unrecognized mode {0}", mode);
|
||||
return;
|
||||
|
@ -571,7 +711,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
string.Format(
|
||||
"Are you sure that you want to delete {0} objects from {1}",
|
||||
deletes.Count, m_scene.RegionInfo.RegionName),
|
||||
"n");
|
||||
"y/N");
|
||||
|
||||
if (response.ToLower() != "y")
|
||||
{
|
||||
|
@ -593,9 +733,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
|
||||
private List<SceneObjectGroup> GetDeleteCandidatesByName(string module, string[] cmdparams)
|
||||
{
|
||||
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
|
||||
return null;
|
||||
|
||||
bool useRegex = false;
|
||||
OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null );
|
||||
|
||||
|
@ -629,5 +766,52 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -267,18 +267,26 @@ namespace OpenSim.Region.Framework.Interfaces
|
|||
|
||||
void ApplyGodPermissions(uint perms);
|
||||
|
||||
/// <summary>
|
||||
/// Number of items in this inventory.
|
||||
/// </summary>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this inventory contains any scripts
|
||||
/// </summary></returns>
|
||||
bool ContainsScripts();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the count of scripts contained
|
||||
/// </summary></returns>
|
||||
/// Number of scripts in this inventory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Includes both running and non running scripts.
|
||||
/// </remarks>
|
||||
int ScriptCount();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the count of running scripts contained
|
||||
/// Number of running scripts in this inventory.
|
||||
/// </summary></returns>
|
||||
int RunningScriptCount();
|
||||
|
||||
|
|
|
@ -121,13 +121,21 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is triggered for both child and root agent client connections.
|
||||
///
|
||||
/// 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>
|
||||
public event OnNewClientDelegate OnNewClient;
|
||||
|
||||
/// <summary>
|
||||
/// Fired if the client entering this sim is doing so as a new login
|
||||
/// </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 delegate void OnNewPresenceDelegate(ScenePresence presence);
|
||||
|
@ -149,6 +157,9 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
/// <remarks>
|
||||
/// 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>
|
||||
///
|
||||
/// 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 OnRemovePresenceDelegate OnRemovePresence;
|
||||
|
||||
|
@ -425,6 +436,9 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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>
|
||||
public event ClientClosed OnClientClosed;
|
||||
|
||||
|
|
|
@ -79,6 +79,11 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
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>
|
||||
/// Statistical information for this scene.
|
||||
/// </summary>
|
||||
|
@ -301,6 +306,31 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
}
|
||||
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 bool m_firstHeartbeat = true;
|
||||
|
||||
|
@ -1154,6 +1184,14 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
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"))
|
||||
{
|
||||
bool enableScripts = true;
|
||||
|
@ -1293,6 +1331,8 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
m_active = true;
|
||||
|
||||
// m_log.DebugFormat("[SCENE]: Starting Heartbeat timer for {0}", RegionInfo.RegionName);
|
||||
|
||||
//m_heartbeatTimer.Enabled = true;
|
||||
|
@ -1334,7 +1374,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
#region Update Methods
|
||||
|
||||
/// <summary>
|
||||
/// Performs per-frame updates regularly
|
||||
/// Activate the various loops necessary to continually update the scene.
|
||||
/// </summary>
|
||||
private void Heartbeat()
|
||||
{
|
||||
|
@ -1391,7 +1431,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
List<Vector3> coarseLocations;
|
||||
List<UUID> avatarUUIDs;
|
||||
|
||||
while (!m_shuttingDown && (endRun == null || MaintenanceRun < endRun))
|
||||
while (!m_shuttingDown && ((endRun == null && Active) || MaintenanceRun < endRun))
|
||||
{
|
||||
runtc = Util.EnvironmentTickCount();
|
||||
++MaintenanceRun;
|
||||
|
@ -1450,7 +1490,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
int previousFrameTick, tmpMS;
|
||||
int maintc = Util.EnvironmentTickCount();
|
||||
|
||||
while (!m_shuttingDown && (endFrame == null || Frame < endFrame))
|
||||
while (!m_shuttingDown && ((endFrame == null && Active) || Frame < endFrame))
|
||||
{
|
||||
++Frame;
|
||||
|
||||
|
@ -2709,69 +2749,89 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type)
|
||||
{
|
||||
ScenePresence sp;
|
||||
bool vialogin;
|
||||
|
||||
// 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);
|
||||
|
||||
bool vialogin
|
||||
= (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0
|
||||
|| (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0;
|
||||
|
||||
// CheckHeartbeat();
|
||||
|
||||
ScenePresence sp = GetScenePresence(client.AgentId);
|
||||
|
||||
// XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this
|
||||
// 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
|
||||
// connected.
|
||||
if (sp == null)
|
||||
// We lock here on AgentCircuitData to prevent a race condition between the thread adding a new connection
|
||||
// and a simultaneous one that removes it (as can happen if the client is closed at a particular point
|
||||
// whilst connecting).
|
||||
//
|
||||
// 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
|
||||
// response in some module listening to AddNewClient()) from holding up unrelated agent calls.
|
||||
//
|
||||
// In practice, the lock (this) in LLUDPServer.AddNewClient() currently lock across all
|
||||
// AddNewClient() operations (though not other ops).
|
||||
// In the future this can be relieved once locking per agent (not necessarily on AgentCircuitData) is improved.
|
||||
lock (aCircuit)
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}",
|
||||
client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos);
|
||||
vialogin
|
||||
= (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0
|
||||
|| (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0;
|
||||
|
||||
// CheckHeartbeat();
|
||||
|
||||
sp = GetScenePresence(client.AgentId);
|
||||
|
||||
m_clientManager.Add(client);
|
||||
SubscribeToClientEvents(client);
|
||||
|
||||
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)
|
||||
// XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this
|
||||
// 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
|
||||
// connected.
|
||||
if (sp == null)
|
||||
{
|
||||
// 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); });
|
||||
m_log.DebugFormat(
|
||||
"[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}",
|
||||
client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos);
|
||||
|
||||
m_clientManager.Add(client);
|
||||
SubscribeToClientEvents(client);
|
||||
|
||||
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
|
||||
{
|
||||
m_log.WarnFormat(
|
||||
"[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);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.WarnFormat(
|
||||
"[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);
|
||||
}
|
||||
|
||||
// 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
|
||||
// client is for a root or child agent.
|
||||
client.SceneAgent = sp;
|
||||
// Cache the user's name
|
||||
CacheUserName(sp, aCircuit);
|
||||
|
||||
EventManager.TriggerOnNewClient(client);
|
||||
if (vialogin)
|
||||
EventManager.TriggerOnClientLogin(client);
|
||||
}
|
||||
|
||||
m_LastLogin = Util.EnvironmentTickCount();
|
||||
|
||||
// Cache the user's name
|
||||
CacheUserName(sp, aCircuit);
|
||||
|
||||
EventManager.TriggerOnNewClient(client);
|
||||
if (vialogin)
|
||||
EventManager.TriggerOnClientLogin(client);
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
|
@ -3300,109 +3360,129 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
{
|
||||
// CheckHeartbeat();
|
||||
bool isChildAgent = false;
|
||||
ScenePresence avatar = GetScenePresence(agentID);
|
||||
AgentCircuitData acd;
|
||||
|
||||
if (avatar == null)
|
||||
lock (m_removeClientLock)
|
||||
{
|
||||
m_log.WarnFormat(
|
||||
"[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID);
|
||||
acd = m_authenticateHandler.GetAgentCircuitData(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;
|
||||
|
||||
m_log.DebugFormat(
|
||||
"[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)
|
||||
ScenePresence avatar = GetScenePresence(agentID);
|
||||
|
||||
if (avatar == null)
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
m_log.WarnFormat(
|
||||
"[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
// 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);
|
||||
isChildAgent = avatar.IsChildAgent;
|
||||
|
||||
m_log.DebugFormat(
|
||||
"[SCENE]: Removing {0} agent {1} {2} from {3}",
|
||||
(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)
|
||||
{
|
||||
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>
|
||||
/// 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>
|
||||
/// <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>
|
||||
/// <returns>True if the region accepts this agent. False if it does not. False will
|
||||
/// also return a reason.</returns>
|
||||
|
@ -3476,10 +3554,20 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// 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="reason">Outputs the reason for the false response on this string</param>
|
||||
/// <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);
|
||||
reason = "Access denied, your viewer is banned by the region owner";
|
||||
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
|
||||
if (vialogin)
|
||||
lock (agent)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (sp == null) // We don't have an [child] agent here already
|
||||
{
|
||||
if (requirePresenceLookup)
|
||||
|
||||
land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y);
|
||||
|
||||
//On login test land permisions
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
} catch (Exception e)
|
||||
{
|
||||
m_log.ErrorFormat(
|
||||
"[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace);
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
CapsModule.SetAgentCapsSeeds(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4047,8 +4144,9 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
return false;
|
||||
}
|
||||
|
||||
// We have to wait until the viewer contacts this region after receiving EAC.
|
||||
// That calls AddNewClient, which finally creates the ScenePresence
|
||||
// We have to wait until the viewer contacts this region
|
||||
// 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);
|
||||
|
||||
if (childAgentUpdate != null)
|
||||
|
|
|
@ -3432,6 +3432,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
/// <remarks>
|
||||
/// When the physics engine has finished with it, the sculpt data is discarded to save memory.
|
||||
/// </remarks>
|
||||
/*
|
||||
public void CheckSculptAndLoad()
|
||||
{
|
||||
if (IsDeleted)
|
||||
|
@ -3447,7 +3448,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
for (int i = 0; i < parts.Length; i++)
|
||||
parts[i].CheckSculptAndLoad();
|
||||
}
|
||||
|
||||
*/
|
||||
/// <summary>
|
||||
/// Set the user group to which this scene object belongs.
|
||||
/// </summary>
|
||||
|
|
|
@ -1014,9 +1014,9 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
{
|
||||
actor.Size = m_shape.Scale;
|
||||
|
||||
if (Shape.SculptEntry)
|
||||
CheckSculptAndLoad();
|
||||
else
|
||||
// if (Shape.SculptEntry)
|
||||
// CheckSculptAndLoad();
|
||||
// else
|
||||
ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor);
|
||||
}
|
||||
}
|
||||
|
@ -1620,12 +1620,13 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
if (userExposed)
|
||||
{
|
||||
/*
|
||||
if (dupe.m_shape.SculptEntry && dupe.m_shape.SculptTexture != UUID.Zero)
|
||||
{
|
||||
ParentGroup.Scene.AssetService.Get(
|
||||
dupe.m_shape.SculptTexture.ToString(), dupe, dupe.AssetReceived);
|
||||
}
|
||||
|
||||
*/
|
||||
bool UsePhysics = ((dupe.Flags & PrimFlags.Physics) != 0);
|
||||
dupe.DoPhysicsPropertyUpdate(UsePhysics, true);
|
||||
}
|
||||
|
@ -1643,6 +1644,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
/// <param name="id">ID of asset received</param>
|
||||
/// <param name="sender">Register</param>
|
||||
/// <param name="asset"></param>
|
||||
/*
|
||||
protected void AssetReceived(string id, Object sender, AssetBase asset)
|
||||
{
|
||||
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",
|
||||
Name, UUID, id);
|
||||
}
|
||||
|
||||
*/
|
||||
/// <summary>
|
||||
/// Do a physics property update for a NINJA joint.
|
||||
/// </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
|
||||
// mesh data.
|
||||
if (Shape.SculptEntry)
|
||||
CheckSculptAndLoad();
|
||||
else
|
||||
// if (Shape.SculptEntry)
|
||||
// CheckSculptAndLoad();
|
||||
// else
|
||||
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.
|
||||
/// </summary>
|
||||
/// <param name="texture">The mesh itself.</param>
|
||||
/*
|
||||
public void SculptTextureCallback(AssetBase texture)
|
||||
{
|
||||
if (m_shape.SculptEntry)
|
||||
|
@ -2538,7 +2541,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/// <summary>
|
||||
/// Send a full update to the client for the given part
|
||||
/// </summary>
|
||||
|
@ -3783,7 +3786,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
public void UpdateExtraParam(ushort type, bool inUse, byte[] data)
|
||||
{
|
||||
m_shape.ReadInUpdateExtraParam(type, inUse, data);
|
||||
|
||||
/*
|
||||
if (type == 0x30)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
if (ParentGroup != null)
|
||||
{
|
||||
ParentGroup.HasGroupChanged = true;
|
||||
|
@ -4025,14 +4028,6 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
if (!wasUsingPhysics)
|
||||
{
|
||||
DoPhysicsPropertyUpdate(UsePhysics, false);
|
||||
|
||||
if (!ParentGroup.IsDeleted)
|
||||
{
|
||||
if (LocalId == ParentGroup.RootPart.LocalId)
|
||||
{
|
||||
ParentGroup.CheckSculptAndLoad();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -4072,14 +4067,6 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
pa.SetMaterial(Material);
|
||||
DoPhysicsPropertyUpdate(UsePhysics, true);
|
||||
|
||||
if (!ParentGroup.IsDeleted)
|
||||
{
|
||||
if (LocalId == ParentGroup.RootPart.LocalId)
|
||||
{
|
||||
ParentGroup.CheckSculptAndLoad();
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
((AggregateScriptEvents & scriptEvents.collision) != 0) ||
|
||||
((AggregateScriptEvents & scriptEvents.collision_end) != 0) ||
|
||||
|
@ -4104,14 +4091,6 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
else // it already has a physical representation
|
||||
{
|
||||
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>
|
||||
/// When the physics engine has finished with it, the sculpt data is discarded to save memory.
|
||||
/// </remarks>
|
||||
/*
|
||||
public void CheckSculptAndLoad()
|
||||
{
|
||||
// m_log.DebugFormat("Processing CheckSculptAndLoad for {0} {1}", Name, LocalId);
|
||||
|
@ -4366,7 +4346,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/// <summary>
|
||||
/// Update the texture entry for this part.
|
||||
/// </summary>
|
||||
|
@ -4604,6 +4584,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
}
|
||||
|
||||
Quaternion rot = Quaternion.Slerp(RotationOffset,APIDTarget,1.0f/(float)m_APIDIterations);
|
||||
rot.Normalize();
|
||||
UpdateRotation(rot);
|
||||
|
||||
m_APIDIterations--;
|
||||
|
|
|
@ -92,6 +92,15 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
QueryScriptStates();
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (m_items)
|
||||
return m_items.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
|
|
|
@ -66,9 +66,9 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
// /// </summary>
|
||||
// private bool m_waitingForObjectAsset;
|
||||
|
||||
public UuidGatherer(IAssetService assetCache)
|
||||
public UuidGatherer(IAssetService assetService)
|
||||
{
|
||||
m_assetService = assetCache;
|
||||
m_assetService = assetService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -146,7 +146,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
|
|||
sb.AppendFormat("Attachments for {0}\n", sp.Name);
|
||||
|
||||
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("Item ID", 36));
|
||||
ct.Columns.Add(new ConsoleDisplayTableColumn("Attach Point", 14));
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using log4net;
|
||||
using Mono.Addins;
|
||||
|
@ -36,6 +37,8 @@ using OpenMetaverse.StructuredData;
|
|||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
|
||||
|
||||
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 List<Scene> m_sceneList = new List<Scene>();
|
||||
private IPresenceService m_presenceService;
|
||||
|
||||
private IMessageTransferModule m_msgTransferModule = null;
|
||||
|
||||
|
@ -54,6 +58,27 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
|
|||
private bool m_groupMessagingEnabled = false;
|
||||
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
|
||||
|
||||
public void Initialise(IConfigSource config)
|
||||
|
@ -83,10 +108,17 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
|
|||
return;
|
||||
}
|
||||
|
||||
m_messageOnlineAgentsOnly = groupsConfig.GetBoolean("MessageOnlineUsersOnly", false);
|
||||
|
||||
if (m_messageOnlineAgentsOnly)
|
||||
m_usersOnlineCache = new ExpiringCache<UUID, PresenceInfo[]>();
|
||||
|
||||
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)
|
||||
|
@ -126,6 +158,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_presenceService == null)
|
||||
m_presenceService = scene.PresenceService;
|
||||
|
||||
m_sceneList.Add(scene);
|
||||
|
||||
|
@ -207,12 +241,42 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
|
|||
public void SendMessageToGroup(GridInstantMessage im, UUID groupID)
|
||||
{
|
||||
List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID), groupID);
|
||||
|
||||
if (m_debugEnabled)
|
||||
m_log.DebugFormat(
|
||||
"[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members",
|
||||
groupID, groupMembers.Count);
|
||||
|
||||
int groupMembersCount = groupMembers.Count;
|
||||
|
||||
if (m_messageOnlineAgentsOnly)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID))
|
||||
|
@ -254,6 +318,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
|
|||
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
|
||||
|
|
|
@ -123,7 +123,36 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
|
|||
public void AddRegion(Scene scene)
|
||||
{
|
||||
if (m_groupsEnabled)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -41,8 +41,6 @@ public class BSCharacter : BSPhysObject
|
|||
|
||||
// private bool _stopped;
|
||||
private OMV.Vector3 _size;
|
||||
private OMV.Vector3 _scale;
|
||||
private PrimitiveBaseShape _pbs;
|
||||
private bool _grabbed;
|
||||
private bool _selected;
|
||||
private OMV.Vector3 _position;
|
||||
|
@ -67,6 +65,10 @@ public class BSCharacter : BSPhysObject
|
|||
private bool _kinematic;
|
||||
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 bool _usePID;
|
||||
private float _PIDTau;
|
||||
|
@ -84,14 +86,18 @@ public class BSCharacter : BSPhysObject
|
|||
_flying = isFlying;
|
||||
_orientation = OMV.Quaternion.Identity;
|
||||
_velocity = OMV.Vector3.Zero;
|
||||
_appliedVelocity = OMV.Vector3.Zero;
|
||||
_buoyancy = ComputeBuoyancyFromFlying(isFlying);
|
||||
_currentFriction = PhysicsScene.Params.avatarStandingFriction;
|
||||
_avatarDensity = PhysicsScene.Params.avatarDensity;
|
||||
|
||||
// The dimensions of the avatar capsule are kept in the scale.
|
||||
// Physics creates a unit capsule which is scaled by the physics engine.
|
||||
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();
|
||||
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.ID = LocalID;
|
||||
|
@ -99,28 +105,22 @@ public class BSCharacter : BSPhysObject
|
|||
shapeData.Position = _position;
|
||||
shapeData.Rotation = _orientation;
|
||||
shapeData.Velocity = _velocity;
|
||||
shapeData.Scale = _scale;
|
||||
shapeData.Size = Scale;
|
||||
shapeData.Scale = Scale;
|
||||
shapeData.Mass = _mass;
|
||||
shapeData.Buoyancy = _buoyancy;
|
||||
shapeData.Static = ShapeData.numericFalse;
|
||||
shapeData.Friction = PhysicsScene.Params.avatarFriction;
|
||||
shapeData.Friction = PhysicsScene.Params.avatarStandingFriction;
|
||||
shapeData.Restitution = PhysicsScene.Params.avatarRestitution;
|
||||
|
||||
// do actual create at taint time
|
||||
PhysicsScene.TaintedObject("BSCharacter.create", delegate()
|
||||
{
|
||||
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#.
|
||||
// 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);
|
||||
SetPhysicalProperties();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -131,53 +131,85 @@ public class BSCharacter : BSPhysObject
|
|||
DetailLog("{0},BSCharacter.Destroy", LocalID);
|
||||
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()
|
||||
{
|
||||
base.RequestPhysicsterseUpdate();
|
||||
}
|
||||
// No one calls this method so I don't know what it could possibly mean
|
||||
public override bool Stopped {
|
||||
get { return false; }
|
||||
}
|
||||
public override bool Stopped { get { return false; } }
|
||||
public override OMV.Vector3 Size {
|
||||
get
|
||||
{
|
||||
// Avatar capsule size is kept in the scale parameter.
|
||||
return new OMV.Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z);
|
||||
return _size;
|
||||
}
|
||||
|
||||
set {
|
||||
// When an avatar's size is set, only the height is changed
|
||||
// and that really only depends on the radius.
|
||||
// When an avatar's size is set, only the height is changed.
|
||||
_size = value;
|
||||
ComputeAvatarScale(_size);
|
||||
|
||||
// TODO: something has to be done with the avatar's vertical position
|
||||
|
||||
ComputeAvatarVolumeAndMass();
|
||||
DetailLog("{0},BSCharacter.setSize,call,scale={1},density={2},volume={3},mass={4}",
|
||||
LocalID, Scale, _avatarDensity, _avatarVolume, MassRaw);
|
||||
|
||||
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 {
|
||||
set { _pbs = value;
|
||||
}
|
||||
public override OMV.Vector3 Scale { get; set; }
|
||||
public override PrimitiveBaseShape Shape
|
||||
{
|
||||
set { BaseShape = value; }
|
||||
}
|
||||
|
||||
public override bool Grabbed {
|
||||
set { _grabbed = value;
|
||||
}
|
||||
set { _grabbed = value; }
|
||||
}
|
||||
public override bool Selected {
|
||||
set { _selected = value;
|
||||
}
|
||||
set { _selected = value; }
|
||||
}
|
||||
public override void CrossingFailure() { return; }
|
||||
public override void link(PhysicsActor obj) { return; }
|
||||
|
@ -204,7 +236,7 @@ public class BSCharacter : BSPhysObject
|
|||
|
||||
public override OMV.Vector3 Position {
|
||||
get {
|
||||
// _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID);
|
||||
// _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
|
||||
return _position;
|
||||
}
|
||||
set {
|
||||
|
@ -214,7 +246,7 @@ public class BSCharacter : BSPhysObject
|
|||
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
|
||||
{
|
||||
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
|
||||
// pushed back to the physics engine. This routine would be used by anyone
|
||||
// who is not already pushing the value.
|
||||
private bool PositionSanityCheck2(bool atTaintTime)
|
||||
private bool PositionSanityCheck(bool inTaintTime)
|
||||
{
|
||||
bool ret = false;
|
||||
if (PositionSanityCheck())
|
||||
|
@ -273,9 +305,9 @@ public class BSCharacter : BSPhysObject
|
|||
BSScene.TaintCallback sanityOperation = delegate()
|
||||
{
|
||||
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();
|
||||
else
|
||||
PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", sanityOperation);
|
||||
|
@ -284,11 +316,7 @@ public class BSCharacter : BSPhysObject
|
|||
return ret;
|
||||
}
|
||||
|
||||
public override float Mass {
|
||||
get {
|
||||
return _mass;
|
||||
}
|
||||
}
|
||||
public override float Mass { get { return _mass; } }
|
||||
|
||||
// used when we only want this prim's mass and not the linkset thing
|
||||
public override float MassRaw { get {return _mass; } }
|
||||
|
@ -301,15 +329,13 @@ public class BSCharacter : BSPhysObject
|
|||
PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
|
||||
BulletSimAPI.SetObjectForce(PhysicsScene.WorldID, LocalID, _force);
|
||||
BulletSimAPI.SetObjectForce2(BSBody.ptr, _force);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override int VehicleType {
|
||||
get { return 0; }
|
||||
set { return; }
|
||||
}
|
||||
// Avatars don't do vehicles
|
||||
public override int VehicleType { get { return 0; } set { return; } }
|
||||
public override void VehicleFloatParam(int param, float value) { }
|
||||
public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
|
||||
public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
|
||||
|
@ -328,10 +354,39 @@ public class BSCharacter : BSPhysObject
|
|||
PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
|
||||
{
|
||||
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 {
|
||||
get { return _torque; }
|
||||
set { _torque = value;
|
||||
|
@ -353,8 +408,8 @@ public class BSCharacter : BSPhysObject
|
|||
// m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
|
||||
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
|
||||
{
|
||||
// _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID);
|
||||
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
|
||||
// _position = BulletSimAPI.GetPosition2(BSBody.ptr);
|
||||
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -382,12 +437,18 @@ public class BSCharacter : BSPhysObject
|
|||
set { _isPhysical = value;
|
||||
}
|
||||
}
|
||||
public override bool IsSolid {
|
||||
get { return true; }
|
||||
}
|
||||
public override bool IsStatic {
|
||||
get { return false; }
|
||||
}
|
||||
public override bool Flying {
|
||||
get { return _flying; }
|
||||
set {
|
||||
_flying = value;
|
||||
// simulate flying by changing the effect of gravity
|
||||
this.Buoyancy = ComputeBuoyancyFromFlying(_flying);
|
||||
Buoyancy = ComputeBuoyancyFromFlying(_flying);
|
||||
}
|
||||
}
|
||||
// Flying is implimented by changing the avatar's buoyancy.
|
||||
|
@ -432,6 +493,10 @@ public class BSCharacter : BSPhysObject
|
|||
get { return _rotationalVelocity; }
|
||||
set { _rotationalVelocity = value; }
|
||||
}
|
||||
public override OMV.Vector3 ForceRotationalVelocity {
|
||||
get { return _rotationalVelocity; }
|
||||
set { _rotationalVelocity = value; }
|
||||
}
|
||||
public override bool Kinematic {
|
||||
get { return _kinematic; }
|
||||
set { _kinematic = value; }
|
||||
|
@ -443,10 +508,19 @@ public class BSCharacter : BSPhysObject
|
|||
PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
|
||||
{
|
||||
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
|
||||
public override OMV.Vector3 PIDTarget {
|
||||
|
@ -507,27 +581,32 @@ public class BSCharacter : BSPhysObject
|
|||
|
||||
private void ComputeAvatarScale(OMV.Vector3 size)
|
||||
{
|
||||
_scale.X = PhysicsScene.Params.avatarCapsuleRadius;
|
||||
_scale.Y = PhysicsScene.Params.avatarCapsuleRadius;
|
||||
// The 'size' given by the simulator is the mid-point of the avatar
|
||||
// and X and Y are unspecified.
|
||||
|
||||
// The 1.15 came from ODE but it seems to cause the avatar to float off the ground
|
||||
// _scale.Z = (_size.Z * 1.15f) - (_scale.X + _scale.Y);
|
||||
_scale.Z = (_size.Z) - (_scale.X + _scale.Y);
|
||||
OMV.Vector3 newScale = OMV.Vector3.Zero;
|
||||
newScale.X = PhysicsScene.Params.avatarCapsuleRadius;
|
||||
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()
|
||||
{
|
||||
_avatarVolume = (float)(
|
||||
Math.PI
|
||||
* _scale.X
|
||||
* _scale.Y // the area of capsule cylinder
|
||||
* _scale.Z // times height of capsule cylinder
|
||||
* Scale.X
|
||||
* Scale.Y // the area of capsule cylinder
|
||||
* Scale.Z // times height of capsule cylinder
|
||||
+ 1.33333333f
|
||||
* Math.PI
|
||||
* _scale.X
|
||||
* Math.Min(_scale.X, _scale.Y)
|
||||
* _scale.Y // plus the volume of the capsule end caps
|
||||
* Scale.X
|
||||
* Math.Min(Scale.X, Scale.Y)
|
||||
* Scale.Y // plus the volume of the capsule end caps
|
||||
);
|
||||
_mass = _avatarDensity * _avatarVolume;
|
||||
}
|
||||
|
@ -542,7 +621,23 @@ public class BSCharacter : BSPhysObject
|
|||
_acceleration = entprop.Acceleration;
|
||||
_rotationalVelocity = entprop.RotationalVelocity;
|
||||
// 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.
|
||||
// base.RequestPhysicsterseUpdate();
|
||||
|
|
|
@ -49,9 +49,16 @@ public abstract class BSConstraint : IDisposable
|
|||
if (m_enabled)
|
||||
{
|
||||
m_enabled = false;
|
||||
bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
|
||||
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;
|
||||
if (m_constraint.ptr != 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -462,7 +462,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
return;
|
||||
|
||||
// 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;
|
||||
BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia);
|
||||
BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr);
|
||||
|
@ -481,7 +481,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
m_lastPositionVector = Prim.ForcePosition;
|
||||
|
||||
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
|
||||
|
||||
// Apply the effect of the linear motor.
|
||||
|
@ -540,7 +540,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// add Gravity and Buoyancy
|
||||
// There is some gravity, make a gravity force vector that is applied after object velocity.
|
||||
// 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
|
||||
|
@ -678,10 +678,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
m_newVelocity.Z = 0;
|
||||
|
||||
// Apply velocity
|
||||
Prim.Velocity = m_newVelocity;
|
||||
Prim.ForceVelocity = m_newVelocity;
|
||||
// apply gravity force
|
||||
// Why is this set here? The physics engine already does gravity.
|
||||
// m_prim.AddForce(grav, false);
|
||||
Prim.AddForce(grav, false, true);
|
||||
|
||||
// Apply friction
|
||||
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
|
||||
|
||||
// Get what the body is doing, this includes 'external' influences
|
||||
Vector3 angularVelocity = Prim.RotationalVelocity;
|
||||
Vector3 angularVelocity = Prim.ForceRotationalVelocity;
|
||||
|
||||
if (m_angularMotorApply > 0)
|
||||
{
|
||||
|
@ -810,7 +810,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
m_lastAngularVelocity -= m_lastAngularVelocity * decayamount;
|
||||
|
||||
// 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);
|
||||
} //end MoveAngular
|
||||
|
@ -862,7 +862,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
private void VDetailLog(string msg, params Object[] args)
|
||||
{
|
||||
if (Prim.PhysicsScene.VehicleLoggingEnabled)
|
||||
Prim.PhysicsScene.PhysicsLogging.Write(msg, args);
|
||||
Prim.PhysicsScene.DetailLog(msg, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,8 +52,8 @@ public class BSLinkset
|
|||
// the physical 'taint' children separately.
|
||||
// After taint processing and before the simulation step, these
|
||||
// two lists must be the same.
|
||||
private List<BSPhysObject> m_children;
|
||||
private List<BSPhysObject> m_taintChildren;
|
||||
private HashSet<BSPhysObject> m_children;
|
||||
private HashSet<BSPhysObject> m_taintChildren;
|
||||
|
||||
// We lock the diddling of linkset classes to prevent any badness.
|
||||
// This locks the modification of the instances of this class. Changes
|
||||
|
@ -90,8 +90,8 @@ public class BSLinkset
|
|||
m_nextLinksetID = 1;
|
||||
PhysicsScene = scene;
|
||||
LinksetRoot = parent;
|
||||
m_children = new List<BSPhysObject>();
|
||||
m_taintChildren = new List<BSPhysObject>();
|
||||
m_children = new HashSet<BSPhysObject>();
|
||||
m_taintChildren = new HashSet<BSPhysObject>();
|
||||
m_mass = parent.MassRaw;
|
||||
}
|
||||
|
||||
|
@ -160,6 +160,28 @@ public class BSLinkset
|
|||
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
|
||||
// for a dynamic 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;
|
||||
}
|
||||
|
||||
// When physical properties are changed the linkset needs to recalculate
|
||||
// its internal properties.
|
||||
// Called at runtime.
|
||||
public void Refresh(BSPhysObject requestor)
|
||||
// If the software is handling the movement of all the objects in a linkset
|
||||
// (like if one doesn't use constraints for static linksets), this is called
|
||||
// when an update for the root of the linkset is received.
|
||||
// Called at taint-time!!
|
||||
public void UpdateProperties(BSPhysObject physObject)
|
||||
{
|
||||
// If there are no children, there can't be any constraints to recompute
|
||||
if (!HasAnyChildren)
|
||||
return;
|
||||
|
||||
// Only the root does the recomputation
|
||||
if (IsRoot(requestor))
|
||||
// The root local properties have been updated. Apply to the children if appropriate.
|
||||
if (IsRoot(physObject) && HasAnyChildren)
|
||||
{
|
||||
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 the one with the dependency is root, must undo all children
|
||||
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},numChild={2}",
|
||||
child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count);
|
||||
foreach (BSPhysObject bpo in m_taintChildren)
|
||||
{
|
||||
PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody);
|
||||
ret = true;
|
||||
}
|
||||
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
|
||||
child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"));
|
||||
|
||||
ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -229,20 +245,16 @@ public class BSLinkset
|
|||
child.LocalID,
|
||||
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
|
||||
child.LocalID, child.BSBody.ptr.ToString("X"));
|
||||
// Remove the dependency on the body of this one
|
||||
if (m_taintChildren.Contains(child))
|
||||
{
|
||||
PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody);
|
||||
ret = true;
|
||||
}
|
||||
// ret = PhysicallyUnlinkAChildFromRoot(LinksetRoot, child);
|
||||
// Despite the function name, this removes any link to the specified object.
|
||||
ret = PhysicallyUnlinkAllChildrenFromRoot(child);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Routine used when rebuilding the body of the root of the linkset
|
||||
// This is called after RemoveAllLinksToRoot() to restore all the constraints.
|
||||
// This is called when the root body has been changed.
|
||||
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||
// this routine will restore the removed constraints.
|
||||
// Called at taint-time!!
|
||||
public void RestoreBodyDependencies(BSPrim child)
|
||||
{
|
||||
|
@ -254,7 +266,7 @@ public class BSLinkset
|
|||
child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count);
|
||||
foreach (BSPhysObject bpo in m_taintChildren)
|
||||
{
|
||||
PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody);
|
||||
PhysicallyLinkAChildToRoot(LinksetRoot, bpo);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -263,7 +275,7 @@ public class BSLinkset
|
|||
LinksetRoot.LocalID,
|
||||
LinksetRoot.LocalID, LinksetRoot.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);
|
||||
|
||||
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
|
||||
BSPhysObject rootx = LinksetRoot; // capture the root as of now
|
||||
BSPhysObject childx = child;
|
||||
|
||||
DetailLog("{0},AddChildToLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
|
||||
rootx.LocalID,
|
||||
rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
|
||||
childx.LocalID, childx.BSBody.ptr.ToString("X"));
|
||||
DetailLog("{0},AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
|
||||
|
||||
PhysicsScene.TaintedObject("AddChildToLinkset", delegate()
|
||||
{
|
||||
DetailLog("{0},AddChildToLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID);
|
||||
// build the physical binding between me and the child
|
||||
m_taintChildren.Add(childx);
|
||||
|
||||
DetailLog("{0},AddChildToLinkset,taint,rID={1},rBody={2},cID={3},cBody={4}",
|
||||
rootx.LocalID,
|
||||
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
|
||||
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;
|
||||
|
@ -378,10 +390,8 @@ public class BSLinkset
|
|||
|
||||
PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate()
|
||||
{
|
||||
if (m_taintChildren.Contains(childx))
|
||||
m_taintChildren.Remove(childx);
|
||||
|
||||
PhysicallyUnlinkAChildFromRoot(rootx, rootx.BSBody, childx, childx.BSBody);
|
||||
m_taintChildren.Remove(child);
|
||||
PhysicallyUnlinkAChildFromRoot(rootx, childx);
|
||||
RecomputeLinksetConstraintVariables();
|
||||
});
|
||||
|
||||
|
@ -396,8 +406,7 @@ public class BSLinkset
|
|||
|
||||
// Create a constraint between me (root of linkset) and the passed prim (the child).
|
||||
// Called at taint time!
|
||||
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BulletBody rootBody,
|
||||
BSPhysObject childPrim, BulletBody childBody)
|
||||
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||
{
|
||||
// Zero motion for children so they don't interpolate
|
||||
childPrim.ZeroMotion();
|
||||
|
@ -409,33 +418,17 @@ public class BSLinkset
|
|||
// real world coordinate of midpoint between the two objects
|
||||
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, rootBody.ptr.ToString("X"),
|
||||
childPrim.LocalID, childBody.ptr.ToString("X"),
|
||||
rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"),
|
||||
childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"),
|
||||
rootPrim.Position, childPrim.Position, midPoint);
|
||||
|
||||
// create a constraint that allows no freedom of movement between the two objects
|
||||
// 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(
|
||||
PhysicsScene.World,
|
||||
rootBody.ptr == IntPtr.Zero ? rootPrim.BSBody : rootBody,
|
||||
childBody.ptr == IntPtr.Zero ? childPrim.BSBody : childBody,
|
||||
midPoint,
|
||||
true,
|
||||
true
|
||||
);
|
||||
PhysicsScene.World, rootPrim.BSBody, childPrim.BSBody, midPoint, true, true );
|
||||
|
||||
/* 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
|
||||
|
@ -452,7 +445,7 @@ public class BSLinkset
|
|||
|
||||
// create a constraint that allows no freedom of movement between the two objects
|
||||
// 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(
|
||||
PhysicsScene.World, rootPrim.Body, childPrim.Body,
|
||||
OMV.Vector3.Zero,
|
||||
|
@ -486,39 +479,44 @@ public class BSLinkset
|
|||
{
|
||||
constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations);
|
||||
}
|
||||
|
||||
RecomputeLinksetConstraintVariables();
|
||||
}
|
||||
|
||||
// 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 bodies that were at unlink time.
|
||||
// Called at taint time!
|
||||
private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BulletBody rootBody,
|
||||
BSPhysObject childPrim, BulletBody childBody)
|
||||
private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||
{
|
||||
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, rootBody.ptr.ToString("X"),
|
||||
childPrim.LocalID, childBody.ptr.ToString("X"));
|
||||
rootPrim.LocalID, rootPrim.BSBody.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
|
||||
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
|
||||
BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
// Remove linkage between myself and any possible children I might have.
|
||||
// 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
|
||||
// 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
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -563,7 +567,8 @@ public class BSLinkset
|
|||
// Invoke the detailed logger and output something if it's enabled.
|
||||
private void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
PhysicsScene.PhysicsLogging.Write(msg, args);
|
||||
if (PhysicsScene.PhysicsLogging.Enabled)
|
||||
PhysicsScene.DetailLog(msg, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ public abstract class BSPhysObject : PhysicsActor
|
|||
TypeName = typeName;
|
||||
|
||||
Linkset = new BSLinkset(PhysicsScene, this);
|
||||
LastAssetBuildFailed = false;
|
||||
|
||||
CollisionCollection = new CollisionEventUpdate();
|
||||
SubscribedEventsMs = 0;
|
||||
|
@ -69,6 +70,23 @@ public abstract class BSPhysObject : PhysicsActor
|
|||
// Reference to the physical shape (btCollisionShape) of this object
|
||||
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.
|
||||
public abstract void ZeroMotion();
|
||||
|
||||
|
@ -85,6 +103,14 @@ public abstract class BSPhysObject : PhysicsActor
|
|||
|
||||
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
|
||||
|
||||
// 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 (SubscribedEvents()) {
|
||||
CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
|
||||
// DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
|
||||
// LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
|
||||
DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
|
||||
LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
|
||||
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Routine to 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.
|
||||
// Send the collected collisions into the simulator.
|
||||
// Called at taint time from within the Step() function thus no locking problems
|
||||
// with CollisionCollection and ObjectsWithNoMoreCollisions.
|
||||
// Return 'true' if there were some actual collisions passed up
|
||||
public virtual bool SendCollisions()
|
||||
{
|
||||
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
|
||||
int nowTime = PhysicsScene.SimulationNowTime;
|
||||
if (nowTime >= NextCollisionOkTime)
|
||||
if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
|
||||
{
|
||||
NextCollisionOkTime = nowTime + SubscribedEventsMs;
|
||||
NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
|
||||
|
||||
// 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.
|
||||
|
@ -207,7 +231,8 @@ public abstract class BSPhysObject : PhysicsActor
|
|||
// High performance detailed logging routine used by the physical objects.
|
||||
protected void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
PhysicsScene.PhysicsLogging.Write(msg, args);
|
||||
if (PhysicsScene.PhysicsLogging.Enabled)
|
||||
PhysicsScene.DetailLog(msg, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,12 +46,10 @@ public sealed class BSPrim : BSPhysObject
|
|||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
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 _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 _isSelected;
|
||||
|
@ -98,12 +96,12 @@ public sealed class BSPrim : BSPhysObject
|
|||
_physicsActorType = (int)ActorTypes.Prim;
|
||||
_position = pos;
|
||||
_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;
|
||||
_buoyancy = 1f;
|
||||
_velocity = OMV.Vector3.Zero;
|
||||
_rotationalVelocity = OMV.Vector3.Zero;
|
||||
_pbs = pbs;
|
||||
BaseShape = pbs;
|
||||
_isPhysical = pisPhysical;
|
||||
_isVolumeDetect = false;
|
||||
_friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material
|
||||
|
@ -160,33 +158,32 @@ public sealed class BSPrim : BSPhysObject
|
|||
get { return _size; }
|
||||
set {
|
||||
_size = value;
|
||||
PhysicsScene.TaintedObject("BSPrim.setSize", delegate()
|
||||
{
|
||||
_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);
|
||||
});
|
||||
ForceBodyShapeRebuild(false);
|
||||
}
|
||||
}
|
||||
// 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>.
|
||||
public OMV.Vector3 Scale
|
||||
{
|
||||
get { return _scale; }
|
||||
set { _scale = value; }
|
||||
}
|
||||
public override OMV.Vector3 Scale { get; set; }
|
||||
|
||||
public override PrimitiveBaseShape Shape {
|
||||
set {
|
||||
_pbs = value;
|
||||
PhysicsScene.TaintedObject("BSPrim.setShape", delegate()
|
||||
{
|
||||
_mass = CalculateMass(); // changing the shape changes the mass
|
||||
CreateGeomAndObject(true);
|
||||
});
|
||||
BaseShape = value;
|
||||
ForceBodyShapeRebuild(false);
|
||||
}
|
||||
}
|
||||
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 {
|
||||
set { _grabbed = value;
|
||||
}
|
||||
|
@ -196,7 +193,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
_isSelected = value;
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
@ -265,6 +262,11 @@ public sealed class BSPrim : BSPhysObject
|
|||
return _position;
|
||||
}
|
||||
set {
|
||||
// If you must push the position into the physics engine, use ForcePosition.
|
||||
if (_position == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_position = value;
|
||||
// TODO: what does it mean to set the position of a child prim?? Rebuild the constraint?
|
||||
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
|
||||
// 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.
|
||||
private bool PositionSanityCheck2(bool atTaintTime)
|
||||
private bool PositionSanityCheck(bool inTaintTime)
|
||||
{
|
||||
bool ret = false;
|
||||
if (PositionSanityCheck())
|
||||
|
@ -332,9 +334,9 @@ public sealed class BSPrim : BSPhysObject
|
|||
BSScene.TaintCallback sanityOperation = delegate()
|
||||
{
|
||||
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();
|
||||
else
|
||||
PhysicsScene.TaintedObject("BSPrim.PositionSanityCheck", sanityOperation);
|
||||
|
@ -453,7 +455,6 @@ public sealed class BSPrim : BSPhysObject
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public override OMV.Vector3 Velocity {
|
||||
get { return _velocity; }
|
||||
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 {
|
||||
get { return _torque; }
|
||||
set { _torque = value;
|
||||
|
@ -490,6 +498,8 @@ public sealed class BSPrim : BSPhysObject
|
|||
return _orientation;
|
||||
}
|
||||
set {
|
||||
if (_orientation == value)
|
||||
return;
|
||||
_orientation = value;
|
||||
// TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint?
|
||||
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
|
||||
private bool IsStatic
|
||||
public override bool IsStatic
|
||||
{
|
||||
get { return _isSelected || !IsPhysical; }
|
||||
}
|
||||
|
||||
// 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; }
|
||||
}
|
||||
|
@ -570,7 +580,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
// Set up the object physicalness (does gravity and collisions move this object)
|
||||
MakeDynamic(IsStatic);
|
||||
|
||||
// Update vehicle specific parameters
|
||||
// Update vehicle specific parameters (after MakeDynamic() so can change physical parameters)
|
||||
_vehicle.Refresh();
|
||||
|
||||
// Arrange for collision events if the simulator wants them
|
||||
|
@ -593,7 +603,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
// Recompute any linkset parameters.
|
||||
// When going from non-physical to physical, this re-enables the constraints that
|
||||
// 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}",
|
||||
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);
|
||||
// There is no inertia in a static object
|
||||
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
|
||||
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);
|
||||
// Start it out sleeping and physical actions could wake it up.
|
||||
// BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING);
|
||||
|
||||
BSBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter;
|
||||
BSBody.collisionMask = CollisionFilterGroups.StaticObjectMask;
|
||||
|
@ -638,12 +656,22 @@ public sealed class BSPrim : BSPhysObject
|
|||
// per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
|
||||
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
|
||||
IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.ptr);
|
||||
OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Mass);
|
||||
BulletSimAPI.SetMassProps2(BSBody.ptr, _mass, inertia);
|
||||
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
|
||||
BulletSimAPI.SetDamping2(BSBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping);
|
||||
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.
|
||||
// Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
|
||||
BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING);
|
||||
BulletSimAPI.Activate2(BSBody.ptr, true);
|
||||
BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
|
||||
// BulletSimAPI.Activate2(BSBody.ptr, true);
|
||||
|
||||
BSBody.collisionFilter = CollisionFilterGroups.ObjectFilter;
|
||||
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 {
|
||||
get { return _kinematic; }
|
||||
set { _kinematic = value;
|
||||
|
@ -786,13 +823,20 @@ public sealed class BSPrim : BSPhysObject
|
|||
_buoyancy = value;
|
||||
PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate()
|
||||
{
|
||||
// DetailLog("{0},BSPrim.SetBuoyancy,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));
|
||||
ForceBuoyancy = _buoyancy;
|
||||
});
|
||||
}
|
||||
}
|
||||
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
|
||||
public override OMV.Vector3 PIDTarget {
|
||||
|
@ -828,6 +872,9 @@ public sealed class BSPrim : BSPhysObject
|
|||
|
||||
private List<OMV.Vector3> m_accumulatedForces = new List<OMV.Vector3>();
|
||||
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
|
||||
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);
|
||||
return;
|
||||
}
|
||||
PhysicsScene.TaintedObject("BSPrim.AddForce", delegate()
|
||||
BSScene.TaintCallback addForceOperation = delegate()
|
||||
{
|
||||
OMV.Vector3 fSum = OMV.Vector3.Zero;
|
||||
lock (m_accumulatedForces)
|
||||
{
|
||||
// Sum the accumulated additional forces for one big force to apply once.
|
||||
foreach (OMV.Vector3 v in m_accumulatedForces)
|
||||
{
|
||||
fSum += v;
|
||||
|
@ -854,7 +902,11 @@ public sealed class BSPrim : BSPhysObject
|
|||
// DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum);
|
||||
// For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object.
|
||||
BulletSimAPI.ApplyCentralForce2(BSBody.ptr, fSum);
|
||||
});
|
||||
};
|
||||
if (inTaintTime)
|
||||
addForceOperation();
|
||||
else
|
||||
PhysicsScene.TaintedObject("BSPrim.AddForce", addForceOperation);
|
||||
}
|
||||
|
||||
public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
|
||||
|
@ -872,19 +924,19 @@ public sealed class BSPrim : BSPhysObject
|
|||
float tmp;
|
||||
|
||||
float returnMass = 0;
|
||||
float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f;
|
||||
float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f;
|
||||
float hollowVolume = hollowAmount * hollowAmount;
|
||||
|
||||
switch (_pbs.ProfileShape)
|
||||
switch (BaseShape.ProfileShape)
|
||||
{
|
||||
case ProfileShape.Square:
|
||||
// default box
|
||||
|
||||
if (_pbs.PathCurve == (byte)Extrusion.Straight)
|
||||
if (BaseShape.PathCurve == (byte)Extrusion.Straight)
|
||||
{
|
||||
if (hollowAmount > 0.0)
|
||||
{
|
||||
switch (_pbs.HollowShape)
|
||||
switch (BaseShape.HollowShape)
|
||||
{
|
||||
case HollowShape.Square:
|
||||
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
|
||||
|
||||
volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX);
|
||||
tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY);
|
||||
volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX);
|
||||
tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY);
|
||||
volume -= volume*tmp*tmp;
|
||||
|
||||
if (hollowAmount > 0.0)
|
||||
{
|
||||
hollowVolume *= hollowAmount;
|
||||
|
||||
switch (_pbs.HollowShape)
|
||||
switch (BaseShape.HollowShape)
|
||||
{
|
||||
case HollowShape.Square:
|
||||
case HollowShape.Same:
|
||||
|
@ -945,13 +997,13 @@ public sealed class BSPrim : BSPhysObject
|
|||
|
||||
case ProfileShape.Circle:
|
||||
|
||||
if (_pbs.PathCurve == (byte)Extrusion.Straight)
|
||||
if (BaseShape.PathCurve == (byte)Extrusion.Straight)
|
||||
{
|
||||
volume *= 0.78539816339f; // elipse base
|
||||
|
||||
if (hollowAmount > 0.0)
|
||||
{
|
||||
switch (_pbs.HollowShape)
|
||||
switch (BaseShape.HollowShape)
|
||||
{
|
||||
case HollowShape.Same:
|
||||
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);
|
||||
tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY);
|
||||
volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX);
|
||||
tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
|
||||
volume *= (1.0f - tmp * tmp);
|
||||
|
||||
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
|
||||
hollowVolume *= hollowAmount;
|
||||
|
||||
switch (_pbs.HollowShape)
|
||||
switch (BaseShape.HollowShape)
|
||||
{
|
||||
case HollowShape.Same:
|
||||
case HollowShape.Circle:
|
||||
|
@ -1009,7 +1061,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
break;
|
||||
|
||||
case ProfileShape.HalfCircle:
|
||||
if (_pbs.PathCurve == (byte)Extrusion.Curve1)
|
||||
if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
|
||||
{
|
||||
volume *= 0.52359877559829887307710723054658f;
|
||||
}
|
||||
|
@ -1017,7 +1069,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
|
||||
case ProfileShape.EquilateralTriangle:
|
||||
|
||||
if (_pbs.PathCurve == (byte)Extrusion.Straight)
|
||||
if (BaseShape.PathCurve == (byte)Extrusion.Straight)
|
||||
{
|
||||
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
|
||||
switch (_pbs.HollowShape)
|
||||
switch (BaseShape.HollowShape)
|
||||
{
|
||||
case HollowShape.Same:
|
||||
case HollowShape.Triangle:
|
||||
|
@ -1050,11 +1102,11 @@ public sealed class BSPrim : BSPhysObject
|
|||
volume *= (1.0f - hollowVolume);
|
||||
}
|
||||
}
|
||||
else if (_pbs.PathCurve == (byte)Extrusion.Curve1)
|
||||
else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
|
||||
{
|
||||
volume *= 0.32475953f;
|
||||
volume *= 0.01f * (float)(200 - _pbs.PathScaleX);
|
||||
tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY);
|
||||
volume *= 0.01f * (float)(200 - BaseShape.PathScaleX);
|
||||
tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
|
||||
volume *= (1.0f - tmp * tmp);
|
||||
|
||||
if (hollowAmount > 0.0)
|
||||
|
@ -1062,7 +1114,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
|
||||
hollowVolume *= hollowAmount;
|
||||
|
||||
switch (_pbs.HollowShape)
|
||||
switch (BaseShape.HollowShape)
|
||||
{
|
||||
case HollowShape.Same:
|
||||
case HollowShape.Triangle:
|
||||
|
@ -1102,26 +1154,26 @@ public sealed class BSPrim : BSPhysObject
|
|||
float profileBegin;
|
||||
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)
|
||||
taperX1 = 2.0f - taperX1;
|
||||
taperX = 1.0f - taperX1;
|
||||
|
||||
taperY1 = _pbs.PathScaleY * 0.01f;
|
||||
taperY1 = BaseShape.PathScaleY * 0.01f;
|
||||
if (taperY1 > 1.0f)
|
||||
taperY1 = 2.0f - taperY1;
|
||||
taperY = 1.0f - taperY1;
|
||||
}
|
||||
else
|
||||
{
|
||||
taperX = _pbs.PathTaperX * 0.01f;
|
||||
taperX = BaseShape.PathTaperX * 0.01f;
|
||||
if (taperX < 0.0f)
|
||||
taperX = -taperX;
|
||||
taperX1 = 1.0f - taperX;
|
||||
|
||||
taperY = _pbs.PathTaperY * 0.01f;
|
||||
taperY = BaseShape.PathTaperY * 0.01f;
|
||||
if (taperY < 0.0f)
|
||||
taperY = -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);
|
||||
|
||||
pathBegin = (float)_pbs.PathBegin * 2.0e-5f;
|
||||
pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f;
|
||||
pathBegin = (float)BaseShape.PathBegin * 2.0e-5f;
|
||||
pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f;
|
||||
volume *= (pathEnd - pathBegin);
|
||||
|
||||
// this is crude aproximation
|
||||
profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f;
|
||||
profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f;
|
||||
profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f;
|
||||
profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
|
||||
volume *= (profileEnd - profileBegin);
|
||||
|
||||
returnMass = _density * volume;
|
||||
|
@ -1172,7 +1224,8 @@ public sealed class BSPrim : BSPhysObject
|
|||
shape.Position = _position;
|
||||
shape.Rotation = _orientation;
|
||||
shape.Velocity = _velocity;
|
||||
shape.Scale = _scale;
|
||||
shape.Size = _size;
|
||||
shape.Scale = Scale;
|
||||
shape.Mass = _isPhysical ? _mass : 0f;
|
||||
shape.Buoyancy = _buoyancy;
|
||||
shape.HullKey = 0;
|
||||
|
@ -1182,7 +1235,6 @@ public sealed class BSPrim : BSPhysObject
|
|||
shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse;
|
||||
shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
|
||||
shape.Solid = IsSolid ? ShapeData.numericFalse : ShapeData.numericTrue;
|
||||
shape.Size = _size;
|
||||
}
|
||||
// Rebuild the geometry and object.
|
||||
// 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.
|
||||
// Updates BSBody and BSShape with the new information.
|
||||
// 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)
|
||||
{
|
||||
// Called if the current prim body is about to be destroyed.
|
||||
// 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);
|
||||
});
|
||||
|
||||
|
@ -1292,7 +1345,11 @@ public sealed class BSPrim : BSPhysObject
|
|||
_acceleration = entprop.Acceleration;
|
||||
_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}",
|
||||
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
|
||||
|
@ -1304,12 +1361,15 @@ public sealed class BSPrim : BSPhysObject
|
|||
/*
|
||||
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}",
|
||||
LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
|
||||
entprop.Acceleration, entprop.RotationalVelocity);
|
||||
}
|
||||
*/
|
||||
// The linkset implimentation might want to know about this.
|
||||
|
||||
Linkset.UpdateProperties(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ using log4net;
|
|||
using OpenMetaverse;
|
||||
|
||||
// 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)
|
||||
// Compute physics FPS reasonably
|
||||
// Based on material, set density and friction
|
||||
|
@ -90,10 +89,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
// let my minuions use my logger
|
||||
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;
|
||||
// Level of Detail values kept as float because that's what the Meshmerizer wants
|
||||
public float MeshLOD { get; private set; }
|
||||
|
@ -112,6 +107,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
private float m_fixedTimeStep;
|
||||
private long m_simulationStep = 0;
|
||||
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
|
||||
// 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 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_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
|
||||
// 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.
|
||||
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
|
||||
|
||||
// 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_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(),
|
||||
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));
|
||||
m_DebugLogCallbackHandle));
|
||||
|
||||
Constraints = new BSConstraintCollection(World);
|
||||
|
||||
|
@ -331,7 +324,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
// Called directly from unmanaged code so don't do much
|
||||
private void BulletLoggerPhysLog(string msg)
|
||||
{
|
||||
PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg);
|
||||
DetailLog("[BULLETS UNMANAGED]:" + msg);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
|
@ -363,7 +356,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
}
|
||||
|
||||
// Anything left in the unmanaged code should be cleaned out
|
||||
BulletSimAPI.Shutdown(WorldID);
|
||||
BulletSimAPI.Shutdown2(World.ptr);
|
||||
|
||||
// Not logging any more
|
||||
PhysicsLogging.Close();
|
||||
|
@ -494,19 +487,19 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
m_simulationStep++;
|
||||
int numSubSteps = 0;
|
||||
|
||||
// Sometimes needed for debugging to find out what happened before the step
|
||||
// PhysicsLogging.Flush();
|
||||
// DEBUG
|
||||
// DetailLog("{0},BSScene.Simulate,beforeStep,ntaimts={1},step={2}", DetailLogZero, numTaints, m_simulationStep);
|
||||
|
||||
try
|
||||
{
|
||||
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);
|
||||
|
||||
if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
|
||||
DetailLog("{0},Simulate,call, nTaints={1}, simTime={2}, substeps={3}, updates={4}, colliders={5}",
|
||||
DetailLogZero, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount);
|
||||
DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}",
|
||||
DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount);
|
||||
}
|
||||
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.
|
||||
// Now push the collisions into the simulator.
|
||||
if (ObjectsWithCollisions.Count > 0)
|
||||
{
|
||||
foreach (BSPhysObject bsp in ObjectsWithCollisions)
|
||||
if (!m_avatars.Contains(bsp)) // don't call avatars twice
|
||||
if (!bsp.SendCollisions())
|
||||
{
|
||||
// If the object is done colliding, see that it's removed from the colliding list
|
||||
ObjectsWithNoMoreCollisions.Add(bsp);
|
||||
}
|
||||
if (!bsp.SendCollisions())
|
||||
{
|
||||
// If the object is done colliding, see that it's removed from the colliding list
|
||||
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.
|
||||
// Not done above because it is inside an iteration of ObjectWithCollisions.
|
||||
if (ObjectsWithNoMoreCollisions.Count > 0)
|
||||
|
@ -582,19 +575,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
}
|
||||
}
|
||||
|
||||
// If enabled, call into the physics engine to dump statistics
|
||||
if (m_detailedStatsStep > 0)
|
||||
{
|
||||
if ((m_simulationStep % m_detailedStatsStep) == 0)
|
||||
{
|
||||
BulletSimAPI.DumpBulletStatistics();
|
||||
}
|
||||
}
|
||||
// This causes the unmanaged code to output ALL the values found in ALL the objects in the world.
|
||||
// Only enable this in a limited test world with few objects.
|
||||
// BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
|
||||
|
||||
// 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.
|
||||
// Since Bullet normally does 5 or 6 substeps, this will normally sum to about 60 FPS.
|
||||
return numSubSteps * m_fixedTimeStep * 1000;
|
||||
// We multiply by 55 to give a recognizable running rate (55 or less).
|
||||
return numSubSteps * m_fixedTimeStep * 1000 * 55;
|
||||
// return timeStep * 1000 * 55;
|
||||
}
|
||||
|
||||
// Something has collided
|
||||
|
@ -617,7 +606,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
BSPhysObject collidee = null;
|
||||
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))
|
||||
{
|
||||
|
@ -703,6 +692,35 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
{
|
||||
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
|
||||
List<TaintCallbackEntry> oldList;
|
||||
lock (_taintLock)
|
||||
|
@ -715,6 +733,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
{
|
||||
try
|
||||
{
|
||||
DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
|
||||
tcbe.callback();
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -723,6 +742,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
}
|
||||
}
|
||||
oldList.Clear();
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -780,6 +800,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
|
||||
delegate float ParamGet(BSScene scene);
|
||||
delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
|
||||
delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
|
||||
|
||||
private struct ParameterDefn
|
||||
{
|
||||
|
@ -789,6 +810,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
public ParamUser userParam; // get the value from the configuration file
|
||||
public ParamGet getter; // return the current value stored 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)
|
||||
{
|
||||
name = n;
|
||||
|
@ -797,6 +819,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
userParam = u;
|
||||
getter = g;
|
||||
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:
|
||||
// s = BSScene
|
||||
// o = BSPhysObject
|
||||
// p = string parameter name
|
||||
// l = localID of referenced object
|
||||
// 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) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); },
|
||||
(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)",
|
||||
8f,
|
||||
|
@ -876,6 +915,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
(s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_maxUpdatesPerFrame; },
|
||||
(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)",
|
||||
10000.01f,
|
||||
(s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); },
|
||||
|
@ -917,70 +961,84 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
-9.80665f,
|
||||
(s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); },
|
||||
(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)",
|
||||
0f,
|
||||
(s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); },
|
||||
(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)",
|
||||
0f,
|
||||
(s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); },
|
||||
(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",
|
||||
0.2f,
|
||||
(s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); },
|
||||
(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",
|
||||
0.8f,
|
||||
(s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); },
|
||||
(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",
|
||||
1.0f,
|
||||
(s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); },
|
||||
(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)" ,
|
||||
0f, // set to zero to disable
|
||||
(s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); },
|
||||
(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" ,
|
||||
0f,
|
||||
(s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); },
|
||||
(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" ,
|
||||
0.1f,
|
||||
(s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); },
|
||||
(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" ,
|
||||
0.5f,
|
||||
(s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
|
||||
(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" ,
|
||||
0.8f,
|
||||
(s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); },
|
||||
(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" ,
|
||||
0f,
|
||||
(s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
|
||||
(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.",
|
||||
0.2f,
|
||||
(s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
|
||||
(s) => { return s.m_params[0].avatarFriction; },
|
||||
(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.",
|
||||
60f,
|
||||
(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,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",
|
||||
0.1f,
|
||||
0.001f,
|
||||
(s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
|
||||
(s) => { return s.m_params[0].linkConstraintCFM; },
|
||||
(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",
|
||||
0.2f,
|
||||
0.8f,
|
||||
(s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
|
||||
(s) => { return s.m_params[0].linkConstraintERP; },
|
||||
(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,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,
|
||||
(s,cf,p,v) => { s.m_detailedStatsStep = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_detailedStatsStep; },
|
||||
(s,p,l,v) => { s.m_detailedStatsStep = (int)v; } ),
|
||||
(s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_params[0].physicsLoggingFrames; },
|
||||
(s,p,l,v) => { s.m_params[0].physicsLoggingFrames = (int)v; } ),
|
||||
};
|
||||
|
||||
// Convert a boolean to our numeric true and false values
|
||||
|
@ -1197,52 +1255,54 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
|||
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
|
||||
// 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 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)
|
||||
{
|
||||
case PhysParameterEntry.APPLY_TO_NONE:
|
||||
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;
|
||||
case PhysParameterEntry.APPLY_TO_ALL:
|
||||
defaultLoc = val; // setting ALL also sets the default value
|
||||
List<uint> objectIDs = lIDs;
|
||||
string xparm = parm.ToLower();
|
||||
float xval = val;
|
||||
TaintedObject("BSScene.UpdateParameterSet", delegate() {
|
||||
foreach (uint lID in objectIDs)
|
||||
{
|
||||
BulletSimAPI.UpdateParameter(WorldID, lID, xparm, xval);
|
||||
}
|
||||
});
|
||||
lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
|
||||
TaintedUpdateParameter(parm, objectIDs, val);
|
||||
break;
|
||||
default:
|
||||
// setting only one localID
|
||||
TaintedUpdateParameter(parm, localID, val);
|
||||
objectIDs.Add(localID);
|
||||
TaintedUpdateParameter(parm, objectIDs, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
TaintedObject("BSScene.TaintedUpdateParameter", delegate() {
|
||||
BulletSimAPI.UpdateParameter(WorldID, xlocalID, xparm, xval);
|
||||
List<uint> xlIDs = lIDs;
|
||||
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)
|
||||
{
|
||||
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
|
||||
public const string DetailLogZero = "0000000000";
|
||||
|
|
|
@ -51,7 +51,7 @@ public class BSShapeCollection : IDisposable
|
|||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
public IntPtr ptr;
|
||||
|
@ -59,17 +59,9 @@ public class BSShapeCollection : IDisposable
|
|||
public DateTime lastReferenced;
|
||||
}
|
||||
|
||||
private struct BodyDesc
|
||||
{
|
||||
public IntPtr ptr;
|
||||
// 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>();
|
||||
// The sharable set of meshes and hulls. Indexed by their shape hash.
|
||||
private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
|
||||
private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
|
||||
|
||||
public BSShapeCollection(BSScene physScene)
|
||||
{
|
||||
|
@ -92,8 +84,12 @@ public class BSShapeCollection : IDisposable
|
|||
// First checks the shape and updates that if necessary then makes
|
||||
// sure the body is of the right type.
|
||||
// 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!!
|
||||
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim,
|
||||
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
|
||||
ShapeData shapeData, PrimitiveBaseShape pbs,
|
||||
ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
|
||||
{
|
||||
|
@ -103,7 +99,8 @@ public class BSShapeCollection : IDisposable
|
|||
lock (m_collectionActivityLock)
|
||||
{
|
||||
// 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);
|
||||
// If we had to select a new shape geometry for the object,
|
||||
// rebuild the body around it.
|
||||
|
@ -120,26 +117,24 @@ public class BSShapeCollection : IDisposable
|
|||
|
||||
// Track another user of a body
|
||||
// We presume the caller has allocated the body.
|
||||
// Bodies only have one user so the reference count is either 1 or 0.
|
||||
public void ReferenceBody(BulletBody body, bool atTaintTime)
|
||||
// Bodies only have one user so the body is just put into the world if not already there.
|
||||
public void ReferenceBody(BulletBody body, bool inTaintTime)
|
||||
{
|
||||
lock (m_collectionActivityLock)
|
||||
{
|
||||
BodyDesc bodyDesc;
|
||||
if (Bodies.TryGetValue(body.ID, out bodyDesc))
|
||||
DetailLog("{0},BSShapeCollection.ReferenceBody,newBody", body.ID, body);
|
||||
BSScene.TaintCallback createOperation = delegate()
|
||||
{
|
||||
bodyDesc.referenceCount++;
|
||||
DetailLog("{0},BSShapeCollection.ReferenceBody,existingBody,body={1},ref={2}", body.ID, body, bodyDesc.referenceCount);
|
||||
}
|
||||
if (!BulletSimAPI.IsInWorld2(body.ptr))
|
||||
{
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
|
||||
DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
|
||||
}
|
||||
};
|
||||
if (inTaintTime)
|
||||
createOperation();
|
||||
else
|
||||
{
|
||||
// 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;
|
||||
PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,42 +147,25 @@ public class BSShapeCollection : IDisposable
|
|||
|
||||
lock (m_collectionActivityLock)
|
||||
{
|
||||
BodyDesc bodyDesc;
|
||||
if (Bodies.TryGetValue(body.ID, out bodyDesc))
|
||||
BSScene.TaintCallback removeOperation = delegate()
|
||||
{
|
||||
bodyDesc.referenceCount--;
|
||||
bodyDesc.lastReferenced = System.DateTime.Now;
|
||||
Bodies[body.ID] = bodyDesc;
|
||||
DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount);
|
||||
DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}",
|
||||
body.ID, body.ptr.ToString("X"), inTaintTime);
|
||||
// If the caller needs to know the old body is going away, pass the event up.
|
||||
if (bodyCallback != null) bodyCallback(body);
|
||||
|
||||
// If body is no longer being used, free it -- bodies are never shared.
|
||||
if (bodyDesc.referenceCount == 0)
|
||||
{
|
||||
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);
|
||||
// It may have already been removed from the world in which case the next is a NOOP.
|
||||
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
|
||||
|
||||
// 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);
|
||||
// It may have already been removed from the world in which case the next is a NOOP.
|
||||
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)
|
||||
removeOperation();
|
||||
else
|
||||
PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
|
||||
}
|
||||
}
|
||||
// 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.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
|
||||
};
|
||||
// If already in taint-time, do the operations now. Otherwise queue for later.
|
||||
if (inTaintTime)
|
||||
removeOperation();
|
||||
else
|
||||
{
|
||||
DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount);
|
||||
}
|
||||
PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,7 +186,7 @@ public class BSShapeCollection : IDisposable
|
|||
{
|
||||
// There is an existing instance of this mesh.
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
@ -217,7 +195,7 @@ public class BSShapeCollection : IDisposable
|
|||
meshDesc.ptr = shape.ptr;
|
||||
// We keep a reference to the underlying IMesh data so a hull can be built
|
||||
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);
|
||||
ret = true;
|
||||
}
|
||||
|
@ -230,7 +208,7 @@ public class BSShapeCollection : IDisposable
|
|||
{
|
||||
// There is an existing instance of this hull.
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
@ -238,7 +216,7 @@ public class BSShapeCollection : IDisposable
|
|||
// This is a new reference to a hull
|
||||
hullDesc.ptr = shape.ptr;
|
||||
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);
|
||||
ret = true;
|
||||
|
||||
|
@ -256,37 +234,42 @@ public class BSShapeCollection : IDisposable
|
|||
}
|
||||
|
||||
// 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 atTaintTime, ShapeDestructionCallback shapeCallback)
|
||||
public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
|
||||
{
|
||||
if (shape.ptr == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
BSScene.TaintCallback dereferenceOperation = delegate()
|
||||
{
|
||||
switch (shape.type)
|
||||
if (shape.ptr != IntPtr.Zero)
|
||||
{
|
||||
case ShapeData.PhysicsShapeType.SHAPE_HULL:
|
||||
DereferenceHull(shape, shapeCallback);
|
||||
break;
|
||||
case ShapeData.PhysicsShapeType.SHAPE_MESH:
|
||||
DereferenceMesh(shape, shapeCallback);
|
||||
break;
|
||||
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
|
||||
break;
|
||||
default:
|
||||
if (shape.isNativeShape)
|
||||
{
|
||||
// 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}",
|
||||
BSScene.DetailLogZero, shape.ptr.ToString("X"), atTaintTime);
|
||||
if (shapeCallback != null) shapeCallback(shape);
|
||||
BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
|
||||
case ShapeData.PhysicsShapeType.SHAPE_HULL:
|
||||
DereferenceHull(shape, shapeCallback);
|
||||
break;
|
||||
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)
|
||||
{
|
||||
|
@ -336,19 +319,31 @@ public class BSShapeCollection : IDisposable
|
|||
|
||||
// Create the geometry information in Bullet for later use.
|
||||
// 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 rebuilt. Otherwise a previously built version is used.
|
||||
// if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
|
||||
// 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.
|
||||
// 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)
|
||||
{
|
||||
bool ret = false;
|
||||
bool haveShape = false;
|
||||
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 (nativeShapePossible
|
||||
if (!haveShape
|
||||
&& pbs != null
|
||||
&& nativeShapePossible
|
||||
&& ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim)
|
||||
|| (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
|
||||
&& pbs.ProfileHollow == 0
|
||||
|
@ -358,7 +353,8 @@ public class BSShapeCollection : IDisposable
|
|||
&& pbs.PathScaleX == 100 && pbs.PathScaleY == 100
|
||||
&& 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;
|
||||
if (forceRebuild
|
||||
|
@ -372,7 +368,7 @@ public class BSShapeCollection : IDisposable
|
|||
prim.LocalID, forceRebuild, prim.BSShape);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
|
||||
{
|
||||
haveShape = true;
|
||||
if (forceRebuild
|
||||
|
@ -390,9 +386,9 @@ public class BSShapeCollection : IDisposable
|
|||
// 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
|
||||
// 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.
|
||||
ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback);
|
||||
|
@ -409,12 +405,12 @@ public class BSShapeCollection : IDisposable
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Creates a native shape and assignes it to prim.BSShape
|
||||
private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData,
|
||||
// Creates a native shape and assignes it to prim.BSShape.
|
||||
// "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,
|
||||
ShapeDestructionCallback shapeCallback)
|
||||
{
|
||||
BulletShape newShape;
|
||||
|
||||
shapeData.Type = shapeType;
|
||||
// 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
|
||||
DereferenceShape(prim.BSShape, true, shapeCallback);
|
||||
|
||||
// Native shapes are always built independently.
|
||||
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
|
||||
newShape.shapeKey = (ulong)shapeKey;
|
||||
newShape.isNativeShape = true;
|
||||
BulletShape newShape = BuildPhysicalNativeShape(shapeType, shapeData, shapeKey);
|
||||
|
||||
// Don't need to do a 'ReferenceShape()' here because native shapes are not tracked.
|
||||
// DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1}", shapeData.ID, newShape);
|
||||
// Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
|
||||
DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
|
||||
shapeData.ID, newShape, shapeData.Scale);
|
||||
|
||||
prim.BSShape = newShape;
|
||||
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.
|
||||
// Dereferences previous shape in BSShape and adds a reference for this new shape.
|
||||
// Returns 'true' of a mesh was actually built. Otherwise .
|
||||
// Called at taint-time!
|
||||
private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs,
|
||||
private bool GetReferenceToMesh(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs,
|
||||
ShapeDestructionCallback shapeCallback)
|
||||
{
|
||||
BulletShape newShape = new BulletShape(IntPtr.Zero);
|
||||
|
||||
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 (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH)
|
||||
|
@ -459,6 +474,8 @@ public class BSShapeCollection : IDisposable
|
|||
DereferenceShape(prim.BSShape, true, shapeCallback);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -469,10 +486,10 @@ public class BSShapeCollection : IDisposable
|
|||
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;
|
||||
IntPtr meshPtr;
|
||||
IntPtr meshPtr = IntPtr.Zero;
|
||||
MeshDesc 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
|
||||
meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
|
||||
|
||||
int[] indices = meshData.getIndexListAsInt();
|
||||
List<OMV.Vector3> vertices = meshData.getVertexList();
|
||||
|
||||
float[] verticesAsFloats = new float[vertices.Count * 3];
|
||||
int vi = 0;
|
||||
foreach (OMV.Vector3 vv in vertices)
|
||||
if (meshData != null)
|
||||
{
|
||||
verticesAsFloats[vi++] = vv.X;
|
||||
verticesAsFloats[vi++] = vv.Y;
|
||||
verticesAsFloats[vi++] = vv.Z;
|
||||
int[] indices = meshData.getIndexListAsInt();
|
||||
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;
|
||||
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);
|
||||
newShape.shapeKey = newMeshKey;
|
||||
|
@ -510,13 +530,13 @@ public class BSShapeCollection : IDisposable
|
|||
|
||||
// See that hull shape exists in the physical world and update prim.BSShape.
|
||||
// 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)
|
||||
{
|
||||
BulletShape newShape;
|
||||
|
||||
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 (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}",
|
||||
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);
|
||||
|
||||
newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod);
|
||||
newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
|
||||
|
||||
ReferenceShape(newShape);
|
||||
|
||||
|
@ -539,10 +560,10 @@ public class BSShapeCollection : IDisposable
|
|||
}
|
||||
|
||||
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;
|
||||
if (Hulls.TryGetValue(newHullKey, out hullDesc))
|
||||
{
|
||||
|
@ -554,86 +575,89 @@ public class BSShapeCollection : IDisposable
|
|||
// 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
|
||||
IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
|
||||
|
||||
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++)
|
||||
if (meshData != null)
|
||||
{
|
||||
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
|
||||
m_hulls = new List<ConvexResult>();
|
||||
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);
|
||||
int[] indices = meshData.getIndexListAsInt();
|
||||
List<OMV.Vector3> vertices = meshData.getVertexList();
|
||||
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
//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++)
|
||||
{
|
||||
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
|
||||
convHulls[jj++] = cr.HullIndices.Count;
|
||||
convHulls[jj++] = 0f; // centroid x,y,z
|
||||
convHulls[jj++] = 0f;
|
||||
convHulls[jj++] = 0f;
|
||||
foreach (int ind in cr.HullIndices)
|
||||
// setup and do convex hull conversion
|
||||
m_hulls = new List<ConvexResult>();
|
||||
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.
|
||||
// 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;
|
||||
convHulls[jj++] = verts[ind].y;
|
||||
convHulls[jj++] = verts[ind].z;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@ -652,32 +676,77 @@ public class BSShapeCollection : IDisposable
|
|||
|
||||
// Create a hash of all the shape parameters to be used as a key
|
||||
// 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
|
||||
float lod = PhysicsScene.MeshLOD;
|
||||
if (pbs.SculptEntry)
|
||||
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));
|
||||
if (maxAxis > PhysicsScene.MeshMegaPrimThreshold)
|
||||
lod = PhysicsScene.MeshMegaPrimLOD;
|
||||
|
||||
retLod = lod;
|
||||
return (ulong)pbs.GetMeshKey(shapeData.Size, lod);
|
||||
return pbs.GetMeshKey(shapeData.Size, 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;
|
||||
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.
|
||||
// Updates prim.BSBody with the information about the new body if one is created.
|
||||
// Returns 'true' if an object was actually created.
|
||||
// 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)
|
||||
{
|
||||
bool ret = false;
|
||||
|
@ -701,6 +770,7 @@ public class BSShapeCollection : IDisposable
|
|||
|
||||
if (mustRebuild || forceRebuild)
|
||||
{
|
||||
// Free any old body
|
||||
DereferenceBody(prim.BSBody, true, bodyCallback);
|
||||
|
||||
BulletBody aBody;
|
||||
|
@ -709,13 +779,13 @@ public class BSShapeCollection : IDisposable
|
|||
{
|
||||
bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
|
||||
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
|
||||
{
|
||||
bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
|
||||
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);
|
||||
|
||||
|
@ -731,7 +801,8 @@ public class BSShapeCollection : IDisposable
|
|||
|
||||
private void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
PhysicsScene.PhysicsLogging.Write(msg, args);
|
||||
if (PhysicsScene.PhysicsLogging.Enabled)
|
||||
PhysicsScene.DetailLog(msg, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,6 +114,9 @@ public class BSTerrainManager
|
|||
BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
|
||||
Vector3.Zero, Quaternion.Identity));
|
||||
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.
|
||||
BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr,
|
||||
(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
|
||||
// calling this routine from initialization or taint-time routines) or whether to delay
|
||||
// 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}",
|
||||
BSScene.DetailLogZero, minCoords, maxCoords, atTaintTime);
|
||||
DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
|
||||
BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
|
||||
|
||||
float minZ = float.MaxValue;
|
||||
float maxZ = float.MinValue;
|
||||
|
@ -296,16 +299,16 @@ public class BSTerrainManager
|
|||
mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID,
|
||||
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
|
||||
Vector3 centerPos;
|
||||
centerPos.X = minCoords.X + (mapInfo.sizeX / 2f);
|
||||
centerPos.Y = minCoords.Y + (mapInfo.sizeY / 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,
|
||||
BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr,
|
||||
id, centerPos, Quaternion.Identity));
|
||||
|
@ -320,9 +323,6 @@ public class BSTerrainManager
|
|||
BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
|
||||
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
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
|
||||
|
||||
|
@ -335,14 +335,15 @@ public class BSTerrainManager
|
|||
|
||||
// Make sure the new shape is processed.
|
||||
// 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;
|
||||
};
|
||||
|
||||
// There is the option to do the changes now (we're already in 'taint time'), or
|
||||
// to do the Bullet operations later.
|
||||
if (atTaintTime)
|
||||
if (inTaintTime)
|
||||
rebuildOperation();
|
||||
else
|
||||
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 (atTaintTime)
|
||||
if (inTaintTime)
|
||||
createOperation();
|
||||
else
|
||||
PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation);
|
||||
|
|
|
@ -101,9 +101,8 @@ public struct BulletShape
|
|||
}
|
||||
public IntPtr ptr;
|
||||
public ShapeData.PhysicsShapeType type;
|
||||
public ulong shapeKey;
|
||||
public System.UInt64 shapeKey;
|
||||
public bool isNativeShape;
|
||||
// Hulls have an underlying mesh. A pointer to it is hidden here.
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
|
@ -192,8 +191,9 @@ public struct ShapeData
|
|||
SHAPE_SPHERE = 5,
|
||||
SHAPE_MESH = 6,
|
||||
SHAPE_HULL = 7,
|
||||
SHAPE_GROUNDPLANE = 8,
|
||||
SHAPE_TERRAIN = 9,
|
||||
// following defined by BulletSim
|
||||
SHAPE_GROUNDPLANE = 20,
|
||||
SHAPE_TERRAIN = 21,
|
||||
};
|
||||
public uint ID;
|
||||
public PhysicsShapeType Type;
|
||||
|
@ -223,6 +223,7 @@ public struct ShapeData
|
|||
KEY_SPHERE = 2,
|
||||
KEY_CONE = 3,
|
||||
KEY_CYLINDER = 4,
|
||||
KEY_CAPSULE = 5,
|
||||
}
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
|
@ -282,6 +283,7 @@ public struct ConfigurationParameters
|
|||
public float terrainHitFraction;
|
||||
public float terrainRestitution;
|
||||
public float avatarFriction;
|
||||
public float avatarStandingFriction;
|
||||
public float avatarDensity;
|
||||
public float avatarRestitution;
|
||||
public float avatarCapsuleRadius;
|
||||
|
@ -305,6 +307,8 @@ public struct ConfigurationParameters
|
|||
public float linkConstraintCFM;
|
||||
public float linkConstraintSolverIterations;
|
||||
|
||||
public float physicsLoggingFrames;
|
||||
|
||||
public const float numericTrue = 1f;
|
||||
public const float numericFalse = 0f;
|
||||
}
|
||||
|
@ -386,7 +390,7 @@ public enum CollisionFilterGroups : uint
|
|||
VolumeDetectMask = ~BSensorTrigger,
|
||||
TerrainFilter = BTerrainFilter,
|
||||
TerrainMask = BAllFilter & ~BStaticFilter,
|
||||
GroundPlaneFilter = BAllFilter,
|
||||
GroundPlaneFilter = BGroundPlaneFilter,
|
||||
GroundPlaneMask = BAllFilter
|
||||
|
||||
};
|
||||
|
@ -426,6 +430,7 @@ public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg
|
|||
[return: MarshalAs(UnmanagedType.LPStr)]
|
||||
public static extern string GetVersion();
|
||||
|
||||
/* Remove the linkage to the old api methods
|
||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||
public static extern uint Initialize(Vector3 maxPosition, IntPtr parms,
|
||||
int maxCollisions, IntPtr collisionArray,
|
||||
|
@ -529,7 +534,7 @@ public static extern Vector3 RecoverFromPenetration(uint worldID, uint id);
|
|||
// ===============================================================================
|
||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||
public static extern void DumpBulletStatistics();
|
||||
|
||||
*/
|
||||
// Log a debug message
|
||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||
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]
|
||||
public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms,
|
||||
int maxCollisions, IntPtr collisionArray,
|
||||
int maxUpdates, IntPtr updateArray);
|
||||
int maxUpdates, IntPtr updateArray,
|
||||
DebugLogCallback logRoutine);
|
||||
|
||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ namespace OpenSim.Region.Physics.Manager
|
|||
get { return new NullPhysicsScene(); }
|
||||
}
|
||||
|
||||
public RequestAssetDelegate RequestAssetMethod { private get; set; }
|
||||
public RequestAssetDelegate RequestAssetMethod { get; set; }
|
||||
|
||||
public virtual void TriggerPhysicsBasedRestart()
|
||||
{
|
||||
|
|
|
@ -66,6 +66,14 @@ namespace OpenSim.Region.Physics.OdePlugin
|
|||
public int ExpectedCollisionContacts { get { return m_expectedCollisionContacts; } }
|
||||
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>
|
||||
/// Is this prim subject to physics? Even if not, it's still solid for collision purposes.
|
||||
/// </summary>
|
||||
|
@ -100,6 +108,9 @@ namespace OpenSim.Region.Physics.OdePlugin
|
|||
private Vector3 m_taintAngularLock = Vector3.One;
|
||||
private IntPtr Amotor = IntPtr.Zero;
|
||||
|
||||
private object m_assetsLock = new object();
|
||||
private bool m_assetFailed = false;
|
||||
|
||||
private Vector3 m_PIDTarget;
|
||||
private float m_PIDTau;
|
||||
private float PID_D = 35f;
|
||||
|
@ -282,6 +293,7 @@ namespace OpenSim.Region.Physics.OdePlugin
|
|||
}
|
||||
|
||||
m_taintadd = true;
|
||||
m_assetFailed = false;
|
||||
_parent_scene.AddPhysicsActorTaint(this);
|
||||
}
|
||||
|
||||
|
@ -337,8 +349,16 @@ namespace OpenSim.Region.Physics.OdePlugin
|
|||
prim_geom = geom;
|
||||
//Console.WriteLine("SetGeom to " + prim_geom + " for " + Name);
|
||||
|
||||
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
|
||||
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
|
||||
if (m_assetFailed)
|
||||
{
|
||||
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.actor_name_map[prim_geom] = this;
|
||||
|
@ -401,8 +421,17 @@ namespace OpenSim.Region.Physics.OdePlugin
|
|||
myrot.W = _orientation.W;
|
||||
d.BodySetQuaternion(Body, ref myrot);
|
||||
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.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
|
||||
|
@ -774,8 +803,16 @@ namespace OpenSim.Region.Physics.OdePlugin
|
|||
m_collisionCategories &= ~CollisionCategories.Body;
|
||||
m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);
|
||||
|
||||
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
|
||||
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
|
||||
if (m_assetFailed)
|
||||
{
|
||||
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);
|
||||
lock (childrenPrim)
|
||||
|
@ -799,8 +836,17 @@ namespace OpenSim.Region.Physics.OdePlugin
|
|||
m_collisionCategories &= ~CollisionCategories.Body;
|
||||
m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);
|
||||
|
||||
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
|
||||
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
|
||||
if (m_assetFailed)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -1090,8 +1136,16 @@ Console.WriteLine("ZProcessTaints for " + Name);
|
|||
prm.m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
|
||||
|
||||
//Console.WriteLine(" GeomSetCategoryBits 1: " + prm.prim_geom + " - " + (int)prm.m_collisionCategories + " for " + Name);
|
||||
d.GeomSetCategoryBits(prm.prim_geom, (int)prm.m_collisionCategories);
|
||||
d.GeomSetCollideBits(prm.prim_geom, (int)prm.m_collisionFlags);
|
||||
if (prm.m_assetFailed)
|
||||
{
|
||||
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();
|
||||
quat.W = prm._orientation.W;
|
||||
|
@ -1136,10 +1190,18 @@ Console.WriteLine("ZProcessTaints for " + Name);
|
|||
m_collisionCategories |= CollisionCategories.Body;
|
||||
m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
|
||||
|
||||
//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);
|
||||
if (m_assetFailed)
|
||||
{
|
||||
d.GeomSetCategoryBits(prim_geom, 0);
|
||||
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();
|
||||
quat2.W = _orientation.W;
|
||||
|
@ -1300,8 +1362,16 @@ Console.WriteLine("ZProcessTaints for " + Name);
|
|||
disableBodySoft();
|
||||
}
|
||||
|
||||
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
|
||||
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
|
||||
if (m_assetFailed)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -1322,8 +1392,16 @@ Console.WriteLine("ZProcessTaints for " + Name);
|
|||
if (m_collidesWater)
|
||||
m_collisionFlags |= CollisionCategories.Water;
|
||||
|
||||
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
|
||||
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
|
||||
if (m_assetFailed)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -1498,6 +1576,10 @@ Console.WriteLine("CreateGeom:");
|
|||
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.
|
||||
// m_log.Debug(m_localID);
|
||||
if (mesh == null)
|
||||
CheckMeshAsset();
|
||||
else
|
||||
m_assetFailed = false;
|
||||
}
|
||||
|
||||
#if SPAM
|
||||
|
@ -1997,7 +2079,14 @@ Console.WriteLine(" JointCreateFixed");
|
|||
// Don't need to re-enable body.. it's done in SetMesh
|
||||
|
||||
if (_parent_scene.needsMeshing(_pbs))
|
||||
{
|
||||
mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical);
|
||||
if (mesh == null)
|
||||
CheckMeshAsset();
|
||||
else
|
||||
m_assetFailed = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CreateGeom(m_targetSpace, mesh);
|
||||
|
@ -2049,14 +2138,19 @@ Console.WriteLine(" JointCreateFixed");
|
|||
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>
|
||||
/// Change prim in response to a shape taint.
|
||||
/// </summary>
|
||||
private void changeshape()
|
||||
{
|
||||
m_taintshape = false;
|
||||
|
||||
// Cleanup of old prim geometry and Bodies
|
||||
if (IsPhysical && Body != IntPtr.Zero)
|
||||
{
|
||||
|
@ -2084,6 +2178,7 @@ Console.WriteLine(" JointCreateFixed");
|
|||
|
||||
IMesh mesh = null;
|
||||
|
||||
|
||||
if (_parent_scene.needsMeshing(_pbs))
|
||||
{
|
||||
// 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.
|
||||
mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical);
|
||||
if (mesh == null)
|
||||
CheckMeshAsset();
|
||||
else
|
||||
m_assetFailed = false;
|
||||
}
|
||||
|
||||
CreateGeom(m_targetSpace, mesh);
|
||||
|
@ -2130,7 +2229,7 @@ Console.WriteLine(" JointCreateFixed");
|
|||
}
|
||||
|
||||
resetCollisionAccounting();
|
||||
m_taintshape = false;
|
||||
// m_taintshape = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -2396,6 +2495,7 @@ Console.WriteLine(" JointCreateFixed");
|
|||
set
|
||||
{
|
||||
_pbs = value;
|
||||
m_assetFailed = false;
|
||||
m_taintshape = true;
|
||||
}
|
||||
}
|
||||
|
@ -3234,5 +3334,37 @@ Console.WriteLine(" JointCreateFixed");
|
|||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4320,4 +4320,4 @@ namespace OpenSim.Region.Physics.OdePlugin
|
|||
m_stats[ODEPrimUpdateFrameMsStatName] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ using GridRegion = OpenSim.Services.Interfaces.GridRegion;
|
|||
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
|
||||
using PrimType = OpenSim.Region.Framework.Scenes.PrimType;
|
||||
using AssetLandmark = OpenSim.Framework.AssetLandmark;
|
||||
using RegionFlags = OpenSim.Framework.RegionFlags;
|
||||
|
||||
using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
|
||||
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
|
||||
|
@ -3772,6 +3773,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
}
|
||||
|
||||
/// <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
|
||||
/// 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
|
||||
|
@ -3799,10 +3810,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
/// Mentions NULL_KEY being returned
|
||||
/// http://wiki.secondlife.com/wiki/LlGetLinkName
|
||||
/// Mentions using the LINK_* constants, some of which are negative
|
||||
/// </summary>
|
||||
/// </remarks>
|
||||
public LSL_String llGetLinkName(int linknum)
|
||||
{
|
||||
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
|
||||
List<String> nametable = new List<String>();
|
||||
World.ForEachRootScenePresence(delegate(ScenePresence presence)
|
||||
|
@ -3826,10 +3841,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
return nametable[totalprims - linknum];
|
||||
}
|
||||
|
||||
// simplest case, this prims link number
|
||||
if (m_host.LinkNum == linknum)
|
||||
return m_host.Name;
|
||||
|
||||
// Single prim
|
||||
if (m_host.LinkNum == 0)
|
||||
{
|
||||
|
@ -6458,7 +6469,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
GridInstantMessage msg = new GridInstantMessage(World,
|
||||
m_host.OwnerID, m_host.Name, destID,
|
||||
(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
|
||||
// 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,
|
||||
|
@ -9313,11 +9324,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
|
||||
GridRegion info;
|
||||
|
||||
if (m_ScriptEngine.World.RegionInfo.RegionName == simulator) //Det data for this simulator?
|
||||
|
||||
info = new GridRegion(m_ScriptEngine.World.RegionInfo);
|
||||
if (World.RegionInfo.RegionName == simulator)
|
||||
info = new GridRegion(World.RegionInfo);
|
||||
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)
|
||||
{
|
||||
|
@ -9327,9 +9337,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
ScriptSleep(1000);
|
||||
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;
|
||||
Utils.LongToUInts(Convert.ToUInt64(info.RegionSecret), out rx, out ry);
|
||||
|
||||
|
@ -9340,7 +9365,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
}
|
||||
else
|
||||
{
|
||||
//Local-cooridnates
|
||||
// Local grid co-oridnates
|
||||
reply = new LSL_Vector(
|
||||
info.RegionLocX,
|
||||
info.RegionLocY,
|
||||
|
|
|
@ -3545,17 +3545,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
/// <returns></returns>
|
||||
public void osSetContentType(LSL_Key id, string type)
|
||||
{
|
||||
CheckThreatLevel(ThreatLevel.High,"osSetResponseType");
|
||||
CheckThreatLevel(ThreatLevel.High, "osSetContentType");
|
||||
|
||||
if (m_UrlModule != null)
|
||||
m_UrlModule.HttpContentType(new UUID(id),type);
|
||||
}
|
||||
|
||||
/// Shout an error if the object owner did not grant the script the specified permissions.
|
||||
/// </summary>
|
||||
/// <param name="perms"></param>
|
||||
/// <returns>boolean indicating whether an error was shouted.</returns>
|
||||
protected bool ShoutErrorOnLackingOwnerPerms(int perms, string errorPrefix)
|
||||
{
|
||||
CheckThreatLevel(ThreatLevel.Moderate, "osDropAttachment");
|
||||
m_host.AddScriptLPS(1);
|
||||
bool fail = false;
|
||||
if (m_item.PermsGranter != m_host.OwnerID)
|
||||
|
|
|
@ -612,6 +612,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
|
|||
public const int CLICK_ACTION_OPEN = 4;
|
||||
public const int CLICK_ACTION_PLAY = 5;
|
||||
public const int CLICK_ACTION_OPEN_MEDIA = 6;
|
||||
public const int CLICK_ACTION_ZOOM = 7;
|
||||
|
||||
// constants for the llDetectedTouch* functions
|
||||
public const int TOUCH_INVALID_FACE = -1;
|
||||
|
|
|
@ -128,7 +128,7 @@ namespace OpenSim.Services.Connectors.Friends
|
|||
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>();
|
||||
//sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString();
|
||||
|
@ -136,7 +136,7 @@ namespace OpenSim.Services.Connectors.Friends
|
|||
sendData["METHOD"] = "status";
|
||||
|
||||
sendData["FromID"] = userID.ToString();
|
||||
sendData["ToID"] = friendID.ToString();
|
||||
sendData["ToID"] = friendID;
|
||||
sendData["Online"] = online.ToString();
|
||||
|
||||
return Call(region, sendData);
|
||||
|
@ -153,7 +153,7 @@ namespace OpenSim.Services.Connectors.Friends
|
|||
if (!region.ServerURI.EndsWith("/"))
|
||||
path = "/" + 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
|
||||
{
|
||||
|
|
|
@ -397,7 +397,7 @@ namespace OpenSim.Services.HypergridService
|
|||
if (region != null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -504,7 +504,7 @@ namespace OpenSim.Services.HypergridService
|
|||
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_FriendsSimConnector.StatusNotify(region, foreignUserID, userID, online);
|
||||
m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,13 +61,49 @@ namespace OpenSim.Services.Interfaces
|
|||
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Remove session information.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <param name='sessionID'></param>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Update data for an existing session.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <param name='sessionID'></param>
|
||||
/// <param name='regionID'></param>
|
||||
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);
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1577,6 +1577,11 @@
|
|||
MessagingModule = GroupsMessagingModule
|
||||
;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
|
||||
|
||||
; SimianGrid Service for Groups
|
||||
|
@ -1602,10 +1607,14 @@
|
|||
|
||||
|
||||
[PacketPool]
|
||||
; Enables the experimental packet pool. Yes, we've been here before.
|
||||
;RecyclePackets = 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]
|
||||
; This section controls how state updates are prioritized for each client
|
||||
|
|
|
@ -21,7 +21,38 @@
|
|||
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
|
||||
; *
|
||||
[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
|
||||
; Set path to directory for plugin registry. Information
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
56
prebuild.xml
56
prebuild.xml
|
@ -113,6 +113,34 @@
|
|||
</Files>
|
||||
</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">
|
||||
<Configuration name="Debug">
|
||||
<Options>
|
||||
|
@ -206,34 +234,6 @@
|
|||
</Files>
|
||||
</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">
|
||||
<Configuration name="Debug">
|
||||
<Options>
|
||||
|
|
Loading…
Reference in New Issue