aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authorBlueWall2012-10-20 05:25:58 -0400
committerBlueWall2012-10-20 05:25:58 -0400
commit07f1d44174f5e2b4e3d5ffdec3619bb84f357af2 (patch)
tree83cc0442b2c1b89a5b3dd94cff23d5098415777e /OpenSim
parentMerge branch 'master' into connector_plugin (diff)
parentFix: invinite loading for Viewer3 : parcelinfo request of traffic-value (impl... (diff)
downloadopensim-SC_OLD-07f1d44174f5e2b4e3d5ffdec3619bb84f357af2.zip
opensim-SC_OLD-07f1d44174f5e2b4e3d5ffdec3619bb84f357af2.tar.gz
opensim-SC_OLD-07f1d44174f5e2b4e3d5ffdec3619bb84f357af2.tar.bz2
opensim-SC_OLD-07f1d44174f5e2b4e3d5ffdec3619bb84f357af2.tar.xz
Merge branch 'master' into connector_plugin
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs6
-rw-r--r--OpenSim/Framework/Console/ConsoleUtil.cs4
-rw-r--r--OpenSim/Framework/IClientAPI.cs15
-rw-r--r--OpenSim/Framework/LandData.cs385
-rw-r--r--OpenSim/Framework/Monitoring/BaseStatsCollector.cs6
-rw-r--r--OpenSim/Framework/Monitoring/MemoryWatchdog.cs10
-rw-r--r--OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs16
-rw-r--r--OpenSim/Framework/Monitoring/StatsManager.cs204
-rw-r--r--OpenSim/Framework/Monitoring/Watchdog.cs20
-rw-r--r--OpenSim/Framework/Pool.cs76
-rw-r--r--OpenSim/Framework/Servers/BaseOpenSimServer.cs32
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs2
-rw-r--r--OpenSim/Region/Application/OpenSim.cs13
-rw-r--r--OpenSim/Region/Application/OpenSimBase.cs72
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs9
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs21
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs2
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs7
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs139
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs227
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs75
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs61
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs7
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs53
-rw-r--r--OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs7
-rw-r--r--OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs2
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs4
-rw-r--r--OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs12
-rw-r--r--OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs328
-rw-r--r--OpenSim/Region/Framework/Interfaces/IEntityInventory.cs14
-rw-r--r--OpenSim/Region/Framework/Scenes/EventManager.cs14
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.cs522
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs3
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneObjectPart.cs53
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs9
-rw-r--r--OpenSim/Region/Framework/Scenes/UuidGatherer.cs4
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs2
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs84
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs29
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs229
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs13
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs16
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs185
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs45
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs220
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs232
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs479
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs27
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs44
-rw-r--r--OpenSim/Region/Physics/Manager/PhysicsScene.cs2
-rw-r--r--OpenSim/Region/Physics/OdePlugin/ODEPrim.cs174
-rw-r--r--OpenSim/Region/Physics/OdePlugin/OdeScene.cs2
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs51
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs5
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs1
-rw-r--r--OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs6
-rw-r--r--OpenSim/Services/HypergridService/HGFriendsService.cs2
-rw-r--r--OpenSim/Services/HypergridService/UserAgentService.cs2
-rw-r--r--OpenSim/Services/Interfaces/IPresenceService.cs38
59 files changed, 2957 insertions, 1365 deletions
diff --git a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
index 9b43a80..04cc33a 100644
--- a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
+++ b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
@@ -223,9 +223,9 @@ namespace OpenSim.Capabilities.Handlers
223 // sending back the last byte instead of an error status 223 // sending back the last byte instead of an error status
224 if (start >= texture.Data.Length) 224 if (start >= texture.Data.Length)
225 { 225 {
226 m_log.DebugFormat( 226// m_log.DebugFormat(
227 "[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}", 227// "[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}",
228 texture.ID, start, texture.Data.Length); 228// texture.ID, start, texture.Data.Length);
229 229
230 // Stricly speaking, as per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, we should be sending back 230 // Stricly speaking, as per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, we should be sending back
231 // Requested Range Not Satisfiable (416) here. However, it appears that at least recent implementations 231 // Requested Range Not Satisfiable (416) here. However, it appears that at least recent implementations
diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs
index 2612a50..a7cf0c0 100644
--- a/OpenSim/Framework/Console/ConsoleUtil.cs
+++ b/OpenSim/Framework/Console/ConsoleUtil.cs
@@ -34,7 +34,7 @@ using OpenMetaverse;
34 34
35public class ConsoleUtil 35public class ConsoleUtil
36{ 36{
37 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 37// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
38 38
39 public const string MinRawConsoleVectorValue = "-~"; 39 public const string MinRawConsoleVectorValue = "-~";
40 public const string MaxRawConsoleVectorValue = "~"; 40 public const string MaxRawConsoleVectorValue = "~";
@@ -107,7 +107,7 @@ public class ConsoleUtil
107 107
108 string semiDigestedConsoleVector = string.Join(VectorSeparator, semiDigestedComponents.ToArray()); 108 string semiDigestedConsoleVector = string.Join(VectorSeparator, semiDigestedComponents.ToArray());
109 109
110 m_log.DebugFormat("[CONSOLE UTIL]: Parsing {0} into OpenMetaverse.Vector3", semiDigestedConsoleVector); 110// m_log.DebugFormat("[CONSOLE UTIL]: Parsing {0} into OpenMetaverse.Vector3", semiDigestedConsoleVector);
111 111
112 return Vector3.TryParse(semiDigestedConsoleVector, out vector); 112 return Vector3.TryParse(semiDigestedConsoleVector, out vector);
113 } 113 }
diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs
index 8a63bff..9856978 100644
--- a/OpenSim/Framework/IClientAPI.cs
+++ b/OpenSim/Framework/IClientAPI.cs
@@ -805,8 +805,23 @@ namespace OpenSim.Framework
805 event Action<IClientAPI> OnRegionHandShakeReply; 805 event Action<IClientAPI> OnRegionHandShakeReply;
806 event GenericCall1 OnRequestWearables; 806 event GenericCall1 OnRequestWearables;
807 event Action<IClientAPI, bool> OnCompleteMovementToRegion; 807 event Action<IClientAPI, bool> OnCompleteMovementToRegion;
808
809 /// <summary>
810 /// Called when an AgentUpdate message is received and before OnAgentUpdate.
811 /// </summary>
812 /// <remarks>
813 /// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates.
814 /// </remarks>
808 event UpdateAgent OnPreAgentUpdate; 815 event UpdateAgent OnPreAgentUpdate;
816
817 /// <summary>
818 /// Called when an AgentUpdate message is received and after OnPreAgentUpdate.
819 /// </summary>
820 /// <remarks>
821 /// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates.
822 /// </remarks>
809 event UpdateAgent OnAgentUpdate; 823 event UpdateAgent OnAgentUpdate;
824
810 event AgentRequestSit OnAgentRequestSit; 825 event AgentRequestSit OnAgentRequestSit;
811 event AgentSit OnAgentSit; 826 event AgentSit OnAgentSit;
812 event AvatarPickerRequest OnAvatarPickerRequest; 827 event AvatarPickerRequest OnAvatarPickerRequest;
diff --git a/OpenSim/Framework/LandData.cs b/OpenSim/Framework/LandData.cs
index bf2ecf2..fc02f33 100644
--- a/OpenSim/Framework/LandData.cs
+++ b/OpenSim/Framework/LandData.cs
@@ -49,8 +49,8 @@ namespace OpenSim.Framework
49 // use only one serializer to give the runtime a chance to 49 // use only one serializer to give the runtime a chance to
50 // optimize it (it won't do that if you use a new instance 50 // optimize it (it won't do that if you use a new instance
51 // every time) 51 // every time)
52 private static XmlSerializer serializer = new XmlSerializer(typeof (LandData)); 52 private static XmlSerializer serializer = new XmlSerializer(typeof(LandData));
53 53
54 private Vector3 _AABBMax = new Vector3(); 54 private Vector3 _AABBMax = new Vector3();
55 private Vector3 _AABBMin = new Vector3(); 55 private Vector3 _AABBMin = new Vector3();
56 private int _area = 0; 56 private int _area = 0;
@@ -65,11 +65,11 @@ namespace OpenSim.Framework
65 private byte[] _bitmap = new byte[512]; 65 private byte[] _bitmap = new byte[512];
66 private string _description = String.Empty; 66 private string _description = String.Empty;
67 67
68 private uint _flags = (uint) ParcelFlags.AllowFly | (uint) ParcelFlags.AllowLandmark | 68 private uint _flags = (uint)ParcelFlags.AllowFly | (uint)ParcelFlags.AllowLandmark |
69 (uint) ParcelFlags.AllowAPrimitiveEntry | 69 (uint)ParcelFlags.AllowAPrimitiveEntry |
70 (uint) ParcelFlags.AllowDeedToGroup | (uint) ParcelFlags.AllowTerraform | 70 (uint)ParcelFlags.AllowDeedToGroup | (uint)ParcelFlags.AllowTerraform |
71 (uint) ParcelFlags.CreateObjects | (uint) ParcelFlags.AllowOtherScripts | 71 (uint)ParcelFlags.CreateObjects | (uint)ParcelFlags.AllowOtherScripts |
72 (uint) ParcelFlags.SoundLocal | (uint) ParcelFlags.AllowVoiceChat; 72 (uint)ParcelFlags.SoundLocal | (uint)ParcelFlags.AllowVoiceChat;
73 73
74 private byte _landingType = 0; 74 private byte _landingType = 0;
75 private string _name = "Your Parcel"; 75 private string _name = "Your Parcel";
@@ -97,16 +97,36 @@ namespace OpenSim.Framework
97 private bool _mediaLoop = false; 97 private bool _mediaLoop = false;
98 private bool _obscureMusic = false; 98 private bool _obscureMusic = false;
99 private bool _obscureMedia = false; 99 private bool _obscureMedia = false;
100 private float _dwell = 0;
101
102 /// <summary>
103 /// Traffic count of parcel
104 /// </summary>
105 [XmlIgnore]
106 public float Dwell
107 {
108 get
109 {
110 return _dwell;
111 }
112 set
113 {
114 _dwell = value;
115 }
116 }
100 117
101 /// <summary> 118 /// <summary>
102 /// Whether to obscure parcel media URL 119 /// Whether to obscure parcel media URL
103 /// </summary> 120 /// </summary>
104 [XmlIgnore] 121 [XmlIgnore]
105 public bool ObscureMedia { 122 public bool ObscureMedia
106 get { 123 {
124 get
125 {
107 return _obscureMedia; 126 return _obscureMedia;
108 } 127 }
109 set { 128 set
129 {
110 _obscureMedia = value; 130 _obscureMedia = value;
111 } 131 }
112 } 132 }
@@ -115,11 +135,14 @@ namespace OpenSim.Framework
115 /// Whether to obscure parcel music URL 135 /// Whether to obscure parcel music URL
116 /// </summary> 136 /// </summary>
117 [XmlIgnore] 137 [XmlIgnore]
118 public bool ObscureMusic { 138 public bool ObscureMusic
119 get { 139 {
140 get
141 {
120 return _obscureMusic; 142 return _obscureMusic;
121 } 143 }
122 set { 144 set
145 {
123 _obscureMusic = value; 146 _obscureMusic = value;
124 } 147 }
125 } 148 }
@@ -128,11 +151,14 @@ namespace OpenSim.Framework
128 /// Whether to loop parcel media 151 /// Whether to loop parcel media
129 /// </summary> 152 /// </summary>
130 [XmlIgnore] 153 [XmlIgnore]
131 public bool MediaLoop { 154 public bool MediaLoop
132 get { 155 {
156 get
157 {
133 return _mediaLoop; 158 return _mediaLoop;
134 } 159 }
135 set { 160 set
161 {
136 _mediaLoop = value; 162 _mediaLoop = value;
137 } 163 }
138 } 164 }
@@ -141,11 +167,14 @@ namespace OpenSim.Framework
141 /// Height of parcel media render 167 /// Height of parcel media render
142 /// </summary> 168 /// </summary>
143 [XmlIgnore] 169 [XmlIgnore]
144 public int MediaHeight { 170 public int MediaHeight
145 get { 171 {
172 get
173 {
146 return _mediaHeight; 174 return _mediaHeight;
147 } 175 }
148 set { 176 set
177 {
149 _mediaHeight = value; 178 _mediaHeight = value;
150 } 179 }
151 } 180 }
@@ -154,11 +183,14 @@ namespace OpenSim.Framework
154 /// Width of parcel media render 183 /// Width of parcel media render
155 /// </summary> 184 /// </summary>
156 [XmlIgnore] 185 [XmlIgnore]
157 public int MediaWidth { 186 public int MediaWidth
158 get { 187 {
188 get
189 {
159 return _mediaWidth; 190 return _mediaWidth;
160 } 191 }
161 set { 192 set
193 {
162 _mediaWidth = value; 194 _mediaWidth = value;
163 } 195 }
164 } 196 }
@@ -167,11 +199,14 @@ namespace OpenSim.Framework
167 /// Upper corner of the AABB for the parcel 199 /// Upper corner of the AABB for the parcel
168 /// </summary> 200 /// </summary>
169 [XmlIgnore] 201 [XmlIgnore]
170 public Vector3 AABBMax { 202 public Vector3 AABBMax
171 get { 203 {
204 get
205 {
172 return _AABBMax; 206 return _AABBMax;
173 } 207 }
174 set { 208 set
209 {
175 _AABBMax = value; 210 _AABBMax = value;
176 } 211 }
177 } 212 }
@@ -179,11 +214,14 @@ namespace OpenSim.Framework
179 /// Lower corner of the AABB for the parcel 214 /// Lower corner of the AABB for the parcel
180 /// </summary> 215 /// </summary>
181 [XmlIgnore] 216 [XmlIgnore]
182 public Vector3 AABBMin { 217 public Vector3 AABBMin
183 get { 218 {
219 get
220 {
184 return _AABBMin; 221 return _AABBMin;
185 } 222 }
186 set { 223 set
224 {
187 _AABBMin = value; 225 _AABBMin = value;
188 } 226 }
189 } 227 }
@@ -191,11 +229,14 @@ namespace OpenSim.Framework
191 /// <summary> 229 /// <summary>
192 /// Area in meters^2 the parcel contains 230 /// Area in meters^2 the parcel contains
193 /// </summary> 231 /// </summary>
194 public int Area { 232 public int Area
195 get { 233 {
234 get
235 {
196 return _area; 236 return _area;
197 } 237 }
198 set { 238 set
239 {
199 _area = value; 240 _area = value;
200 } 241 }
201 } 242 }
@@ -203,11 +244,14 @@ namespace OpenSim.Framework
203 /// <summary> 244 /// <summary>
204 /// ID of auction (3rd Party Integration) when parcel is being auctioned 245 /// ID of auction (3rd Party Integration) when parcel is being auctioned
205 /// </summary> 246 /// </summary>
206 public uint AuctionID { 247 public uint AuctionID
207 get { 248 {
249 get
250 {
208 return _auctionID; 251 return _auctionID;
209 } 252 }
210 set { 253 set
254 {
211 _auctionID = value; 255 _auctionID = value;
212 } 256 }
213 } 257 }
@@ -215,11 +259,14 @@ namespace OpenSim.Framework
215 /// <summary> 259 /// <summary>
216 /// UUID of authorized buyer of parcel. This is UUID.Zero if anyone can buy it. 260 /// UUID of authorized buyer of parcel. This is UUID.Zero if anyone can buy it.
217 /// </summary> 261 /// </summary>
218 public UUID AuthBuyerID { 262 public UUID AuthBuyerID
219 get { 263 {
264 get
265 {
220 return _authBuyerID; 266 return _authBuyerID;
221 } 267 }
222 set { 268 set
269 {
223 _authBuyerID = value; 270 _authBuyerID = value;
224 } 271 }
225 } 272 }
@@ -227,11 +274,14 @@ namespace OpenSim.Framework
227 /// <summary> 274 /// <summary>
228 /// Category of parcel. Used for classifying the parcel in classified listings 275 /// Category of parcel. Used for classifying the parcel in classified listings
229 /// </summary> 276 /// </summary>
230 public ParcelCategory Category { 277 public ParcelCategory Category
231 get { 278 {
279 get
280 {
232 return _category; 281 return _category;
233 } 282 }
234 set { 283 set
284 {
235 _category = value; 285 _category = value;
236 } 286 }
237 } 287 }
@@ -239,11 +289,14 @@ namespace OpenSim.Framework
239 /// <summary> 289 /// <summary>
240 /// Date that the current owner purchased or claimed the parcel 290 /// Date that the current owner purchased or claimed the parcel
241 /// </summary> 291 /// </summary>
242 public int ClaimDate { 292 public int ClaimDate
243 get { 293 {
294 get
295 {
244 return _claimDate; 296 return _claimDate;
245 } 297 }
246 set { 298 set
299 {
247 _claimDate = value; 300 _claimDate = value;
248 } 301 }
249 } 302 }
@@ -251,11 +304,14 @@ namespace OpenSim.Framework
251 /// <summary> 304 /// <summary>
252 /// The last price that the parcel was sold at 305 /// The last price that the parcel was sold at
253 /// </summary> 306 /// </summary>
254 public int ClaimPrice { 307 public int ClaimPrice
255 get { 308 {
309 get
310 {
256 return _claimPrice; 311 return _claimPrice;
257 } 312 }
258 set { 313 set
314 {
259 _claimPrice = value; 315 _claimPrice = value;
260 } 316 }
261 } 317 }
@@ -263,11 +319,14 @@ namespace OpenSim.Framework
263 /// <summary> 319 /// <summary>
264 /// Global ID for the parcel. (3rd Party Integration) 320 /// Global ID for the parcel. (3rd Party Integration)
265 /// </summary> 321 /// </summary>
266 public UUID GlobalID { 322 public UUID GlobalID
267 get { 323 {
324 get
325 {
268 return _globalID; 326 return _globalID;
269 } 327 }
270 set { 328 set
329 {
271 _globalID = value; 330 _globalID = value;
272 } 331 }
273 } 332 }
@@ -275,11 +334,14 @@ namespace OpenSim.Framework
275 /// <summary> 334 /// <summary>
276 /// Unique ID of the Group that owns 335 /// Unique ID of the Group that owns
277 /// </summary> 336 /// </summary>
278 public UUID GroupID { 337 public UUID GroupID
279 get { 338 {
339 get
340 {
280 return _groupID; 341 return _groupID;
281 } 342 }
282 set { 343 set
344 {
283 _groupID = value; 345 _groupID = value;
284 } 346 }
285 } 347 }
@@ -287,11 +349,14 @@ namespace OpenSim.Framework
287 /// <summary> 349 /// <summary>
288 /// Returns true if the Land Parcel is owned by a group 350 /// Returns true if the Land Parcel is owned by a group
289 /// </summary> 351 /// </summary>
290 public bool IsGroupOwned { 352 public bool IsGroupOwned
291 get { 353 {
354 get
355 {
292 return _isGroupOwned; 356 return _isGroupOwned;
293 } 357 }
294 set { 358 set
359 {
295 _isGroupOwned = value; 360 _isGroupOwned = value;
296 } 361 }
297 } 362 }
@@ -299,11 +364,14 @@ namespace OpenSim.Framework
299 /// <summary> 364 /// <summary>
300 /// jp2 data for the image representative of the parcel in the parcel dialog 365 /// jp2 data for the image representative of the parcel in the parcel dialog
301 /// </summary> 366 /// </summary>
302 public byte[] Bitmap { 367 public byte[] Bitmap
303 get { 368 {
369 get
370 {
304 return _bitmap; 371 return _bitmap;
305 } 372 }
306 set { 373 set
374 {
307 _bitmap = value; 375 _bitmap = value;
308 } 376 }
309 } 377 }
@@ -311,11 +379,14 @@ namespace OpenSim.Framework
311 /// <summary> 379 /// <summary>
312 /// Parcel Description 380 /// Parcel Description
313 /// </summary> 381 /// </summary>
314 public string Description { 382 public string Description
315 get { 383 {
384 get
385 {
316 return _description; 386 return _description;
317 } 387 }
318 set { 388 set
389 {
319 _description = value; 390 _description = value;
320 } 391 }
321 } 392 }
@@ -323,11 +394,14 @@ namespace OpenSim.Framework
323 /// <summary> 394 /// <summary>
324 /// Parcel settings. Access flags, Fly, NoPush, Voice, Scripts allowed, etc. ParcelFlags 395 /// Parcel settings. Access flags, Fly, NoPush, Voice, Scripts allowed, etc. ParcelFlags
325 /// </summary> 396 /// </summary>
326 public uint Flags { 397 public uint Flags
327 get { 398 {
399 get
400 {
328 return _flags; 401 return _flags;
329 } 402 }
330 set { 403 set
404 {
331 _flags = value; 405 _flags = value;
332 } 406 }
333 } 407 }
@@ -336,11 +410,14 @@ namespace OpenSim.Framework
336 /// Determines if people are able to teleport where they please on the parcel or if they 410 /// Determines if people are able to teleport where they please on the parcel or if they
337 /// get constrainted to a specific point on teleport within the parcel 411 /// get constrainted to a specific point on teleport within the parcel
338 /// </summary> 412 /// </summary>
339 public byte LandingType { 413 public byte LandingType
340 get { 414 {
415 get
416 {
341 return _landingType; 417 return _landingType;
342 } 418 }
343 set { 419 set
420 {
344 _landingType = value; 421 _landingType = value;
345 } 422 }
346 } 423 }
@@ -348,11 +425,14 @@ namespace OpenSim.Framework
348 /// <summary> 425 /// <summary>
349 /// Parcel Name 426 /// Parcel Name
350 /// </summary> 427 /// </summary>
351 public string Name { 428 public string Name
352 get { 429 {
430 get
431 {
353 return _name; 432 return _name;
354 } 433 }
355 set { 434 set
435 {
356 _name = value; 436 _name = value;
357 } 437 }
358 } 438 }
@@ -360,11 +440,14 @@ namespace OpenSim.Framework
360 /// <summary> 440 /// <summary>
361 /// Status of Parcel, Leased, Abandoned, For Sale 441 /// Status of Parcel, Leased, Abandoned, For Sale
362 /// </summary> 442 /// </summary>
363 public ParcelStatus Status { 443 public ParcelStatus Status
364 get { 444 {
445 get
446 {
365 return _status; 447 return _status;
366 } 448 }
367 set { 449 set
450 {
368 _status = value; 451 _status = value;
369 } 452 }
370 } 453 }
@@ -372,11 +455,14 @@ namespace OpenSim.Framework
372 /// <summary> 455 /// <summary>
373 /// Internal ID of the parcel. Sometimes the client will try to use this value 456 /// Internal ID of the parcel. Sometimes the client will try to use this value
374 /// </summary> 457 /// </summary>
375 public int LocalID { 458 public int LocalID
376 get { 459 {
460 get
461 {
377 return _localID; 462 return _localID;
378 } 463 }
379 set { 464 set
465 {
380 _localID = value; 466 _localID = value;
381 } 467 }
382 } 468 }
@@ -384,11 +470,14 @@ namespace OpenSim.Framework
384 /// <summary> 470 /// <summary>
385 /// Determines if we scale the media based on the surface it's on 471 /// Determines if we scale the media based on the surface it's on
386 /// </summary> 472 /// </summary>
387 public byte MediaAutoScale { 473 public byte MediaAutoScale
388 get { 474 {
475 get
476 {
389 return _mediaAutoScale; 477 return _mediaAutoScale;
390 } 478 }
391 set { 479 set
480 {
392 _mediaAutoScale = value; 481 _mediaAutoScale = value;
393 } 482 }
394 } 483 }
@@ -396,11 +485,14 @@ namespace OpenSim.Framework
396 /// <summary> 485 /// <summary>
397 /// Texture Guid to replace with the output of the media stream 486 /// Texture Guid to replace with the output of the media stream
398 /// </summary> 487 /// </summary>
399 public UUID MediaID { 488 public UUID MediaID
400 get { 489 {
490 get
491 {
401 return _mediaID; 492 return _mediaID;
402 } 493 }
403 set { 494 set
495 {
404 _mediaID = value; 496 _mediaID = value;
405 } 497 }
406 } 498 }
@@ -408,11 +500,14 @@ namespace OpenSim.Framework
408 /// <summary> 500 /// <summary>
409 /// URL to the media file to display 501 /// URL to the media file to display
410 /// </summary> 502 /// </summary>
411 public string MediaURL { 503 public string MediaURL
412 get { 504 {
505 get
506 {
413 return _mediaURL; 507 return _mediaURL;
414 } 508 }
415 set { 509 set
510 {
416 _mediaURL = value; 511 _mediaURL = value;
417 } 512 }
418 } 513 }
@@ -432,11 +527,14 @@ namespace OpenSim.Framework
432 /// <summary> 527 /// <summary>
433 /// URL to the shoutcast music stream to play on the parcel 528 /// URL to the shoutcast music stream to play on the parcel
434 /// </summary> 529 /// </summary>
435 public string MusicURL { 530 public string MusicURL
436 get { 531 {
532 get
533 {
437 return _musicURL; 534 return _musicURL;
438 } 535 }
439 set { 536 set
537 {
440 _musicURL = value; 538 _musicURL = value;
441 } 539 }
442 } 540 }
@@ -445,11 +543,14 @@ namespace OpenSim.Framework
445 /// Owner Avatar or Group of the parcel. Naturally, all land masses must be 543 /// Owner Avatar or Group of the parcel. Naturally, all land masses must be
446 /// owned by someone 544 /// owned by someone
447 /// </summary> 545 /// </summary>
448 public UUID OwnerID { 546 public UUID OwnerID
449 get { 547 {
548 get
549 {
450 return _ownerID; 550 return _ownerID;
451 } 551 }
452 set { 552 set
553 {
453 _ownerID = value; 554 _ownerID = value;
454 } 555 }
455 } 556 }
@@ -457,11 +558,14 @@ namespace OpenSim.Framework
457 /// <summary> 558 /// <summary>
458 /// List of access data for the parcel. User data, some bitflags, and a time 559 /// List of access data for the parcel. User data, some bitflags, and a time
459 /// </summary> 560 /// </summary>
460 public List<LandAccessEntry> ParcelAccessList { 561 public List<LandAccessEntry> ParcelAccessList
461 get { 562 {
563 get
564 {
462 return _parcelAccessList; 565 return _parcelAccessList;
463 } 566 }
464 set { 567 set
568 {
465 _parcelAccessList = value; 569 _parcelAccessList = value;
466 } 570 }
467 } 571 }
@@ -469,11 +573,14 @@ namespace OpenSim.Framework
469 /// <summary> 573 /// <summary>
470 /// How long in hours a Pass to the parcel is given 574 /// How long in hours a Pass to the parcel is given
471 /// </summary> 575 /// </summary>
472 public float PassHours { 576 public float PassHours
473 get { 577 {
578 get
579 {
474 return _passHours; 580 return _passHours;
475 } 581 }
476 set { 582 set
583 {
477 _passHours = value; 584 _passHours = value;
478 } 585 }
479 } 586 }
@@ -481,11 +588,14 @@ namespace OpenSim.Framework
481 /// <summary> 588 /// <summary>
482 /// Price to purchase a Pass to a restricted parcel 589 /// Price to purchase a Pass to a restricted parcel
483 /// </summary> 590 /// </summary>
484 public int PassPrice { 591 public int PassPrice
485 get { 592 {
593 get
594 {
486 return _passPrice; 595 return _passPrice;
487 } 596 }
488 set { 597 set
598 {
489 _passPrice = value; 599 _passPrice = value;
490 } 600 }
491 } 601 }
@@ -493,11 +603,14 @@ namespace OpenSim.Framework
493 /// <summary> 603 /// <summary>
494 /// When the parcel is being sold, this is the price to purchase the parcel 604 /// When the parcel is being sold, this is the price to purchase the parcel
495 /// </summary> 605 /// </summary>
496 public int SalePrice { 606 public int SalePrice
497 get { 607 {
608 get
609 {
498 return _salePrice; 610 return _salePrice;
499 } 611 }
500 set { 612 set
613 {
501 _salePrice = value; 614 _salePrice = value;
502 } 615 }
503 } 616 }
@@ -506,11 +619,14 @@ namespace OpenSim.Framework
506 /// Number of meters^2 in the Simulator 619 /// Number of meters^2 in the Simulator
507 /// </summary> 620 /// </summary>
508 [XmlIgnore] 621 [XmlIgnore]
509 public int SimwideArea { 622 public int SimwideArea
510 get { 623 {
624 get
625 {
511 return _simwideArea; 626 return _simwideArea;
512 } 627 }
513 set { 628 set
629 {
514 _simwideArea = value; 630 _simwideArea = value;
515 } 631 }
516 } 632 }
@@ -519,11 +635,14 @@ namespace OpenSim.Framework
519 /// Number of SceneObjectPart in the Simulator 635 /// Number of SceneObjectPart in the Simulator
520 /// </summary> 636 /// </summary>
521 [XmlIgnore] 637 [XmlIgnore]
522 public int SimwidePrims { 638 public int SimwidePrims
523 get { 639 {
640 get
641 {
524 return _simwidePrims; 642 return _simwidePrims;
525 } 643 }
526 set { 644 set
645 {
527 _simwidePrims = value; 646 _simwidePrims = value;
528 } 647 }
529 } 648 }
@@ -531,11 +650,14 @@ namespace OpenSim.Framework
531 /// <summary> 650 /// <summary>
532 /// ID of the snapshot used in the client parcel dialog of the parcel 651 /// ID of the snapshot used in the client parcel dialog of the parcel
533 /// </summary> 652 /// </summary>
534 public UUID SnapshotID { 653 public UUID SnapshotID
535 get { 654 {
655 get
656 {
536 return _snapshotID; 657 return _snapshotID;
537 } 658 }
538 set { 659 set
660 {
539 _snapshotID = value; 661 _snapshotID = value;
540 } 662 }
541 } 663 }
@@ -544,11 +666,14 @@ namespace OpenSim.Framework
544 /// When teleporting is restricted to a certain point, this is the location 666 /// When teleporting is restricted to a certain point, this is the location
545 /// that the user will be redirected to 667 /// that the user will be redirected to
546 /// </summary> 668 /// </summary>
547 public Vector3 UserLocation { 669 public Vector3 UserLocation
548 get { 670 {
671 get
672 {
549 return _userLocation; 673 return _userLocation;
550 } 674 }
551 set { 675 set
676 {
552 _userLocation = value; 677 _userLocation = value;
553 } 678 }
554 } 679 }
@@ -557,11 +682,14 @@ namespace OpenSim.Framework
557 /// When teleporting is restricted to a certain point, this is the rotation 682 /// When teleporting is restricted to a certain point, this is the rotation
558 /// that the user will be positioned 683 /// that the user will be positioned
559 /// </summary> 684 /// </summary>
560 public Vector3 UserLookAt { 685 public Vector3 UserLookAt
561 get { 686 {
687 get
688 {
562 return _userLookAt; 689 return _userLookAt;
563 } 690 }
564 set { 691 set
692 {
565 _userLookAt = value; 693 _userLookAt = value;
566 } 694 }
567 } 695 }
@@ -570,11 +698,14 @@ namespace OpenSim.Framework
570 /// Autoreturn number of minutes to return SceneObjectGroup that are owned by someone who doesn't own 698 /// Autoreturn number of minutes to return SceneObjectGroup that are owned by someone who doesn't own
571 /// the parcel and isn't set to the same 'group' as the parcel. 699 /// the parcel and isn't set to the same 'group' as the parcel.
572 /// </summary> 700 /// </summary>
573 public int OtherCleanTime { 701 public int OtherCleanTime
574 get { 702 {
703 get
704 {
575 return _otherCleanTime; 705 return _otherCleanTime;
576 } 706 }
577 set { 707 set
708 {
578 _otherCleanTime = value; 709 _otherCleanTime = value;
579 } 710 }
580 } 711 }
@@ -582,11 +713,14 @@ namespace OpenSim.Framework
582 /// <summary> 713 /// <summary>
583 /// parcel media description 714 /// parcel media description
584 /// </summary> 715 /// </summary>
585 public string MediaDescription { 716 public string MediaDescription
586 get { 717 {
718 get
719 {
587 return _mediaDescription; 720 return _mediaDescription;
588 } 721 }
589 set { 722 set
723 {
590 _mediaDescription = value; 724 _mediaDescription = value;
591 } 725 }
592 } 726 }
@@ -622,7 +756,7 @@ namespace OpenSim.Framework
622 landData._mediaURL = _mediaURL; 756 landData._mediaURL = _mediaURL;
623 landData._musicURL = _musicURL; 757 landData._musicURL = _musicURL;
624 landData._ownerID = _ownerID; 758 landData._ownerID = _ownerID;
625 landData._bitmap = (byte[]) _bitmap.Clone(); 759 landData._bitmap = (byte[])_bitmap.Clone();
626 landData._description = _description; 760 landData._description = _description;
627 landData._flags = _flags; 761 landData._flags = _flags;
628 landData._name = _name; 762 landData._name = _name;
@@ -643,6 +777,7 @@ namespace OpenSim.Framework
643 landData._obscureMedia = _obscureMedia; 777 landData._obscureMedia = _obscureMedia;
644 landData._simwideArea = _simwideArea; 778 landData._simwideArea = _simwideArea;
645 landData._simwidePrims = _simwidePrims; 779 landData._simwidePrims = _simwidePrims;
780 landData._dwell = _dwell;
646 781
647 landData._parcelAccessList.Clear(); 782 landData._parcelAccessList.Clear();
648 foreach (LandAccessEntry entry in _parcelAccessList) 783 foreach (LandAccessEntry entry in _parcelAccessList)
diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
index 57a63ef..2903b6e 100644
--- a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
@@ -49,7 +49,11 @@ namespace OpenSim.Framework.Monitoring
49 Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)); 49 Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
50 50
51 sb.AppendFormat( 51 sb.AppendFormat(
52 "OpenSim object memory churn : {0} MB/s\n", 52 "OpenSim last object memory churn : {0} MB/s\n",
53 Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3));
54
55 sb.AppendFormat(
56 "OpenSim average object memory churn : {0} MB/s\n",
53 Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3)); 57 Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3));
54 58
55 sb.AppendFormat( 59 sb.AppendFormat(
diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
index a23cf1f..c6010cd 100644
--- a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
+++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
@@ -60,7 +60,7 @@ namespace OpenSim.Framework.Monitoring
60 private static bool m_enabled; 60 private static bool m_enabled;
61 61
62 /// <summary> 62 /// <summary>
63 /// Average memory churn in bytes per millisecond. 63 /// Last memory churn in bytes per millisecond.
64 /// </summary> 64 /// </summary>
65 public static double AverageMemoryChurn 65 public static double AverageMemoryChurn
66 { 66 {
@@ -68,6 +68,14 @@ namespace OpenSim.Framework.Monitoring
68 } 68 }
69 69
70 /// <summary> 70 /// <summary>
71 /// Average memory churn in bytes per millisecond.
72 /// </summary>
73 public static double LastMemoryChurn
74 {
75 get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; }
76 }
77
78 /// <summary>
71 /// Maximum number of statistical samples. 79 /// Maximum number of statistical samples.
72 /// </summary> 80 /// </summary>
73 /// <remarks> 81 /// <remarks>
diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
index 8ac9090..aa86202 100644
--- a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
@@ -359,13 +359,19 @@ Asset service request failures: {3}" + Environment.NewLine,
359 inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime, 359 inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime,
360 netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime)); 360 netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime));
361 361
362 foreach (KeyValuePair<string, Stat> kvp in StatsManager.RegisteredStats) 362 Dictionary<string, Dictionary<string, Stat>> sceneStats;
363 {
364 Stat stat = kvp.Value;
365 363
366 if (stat.Category == "scene" && stat.Verbosity == StatVerbosity.Info) 364 if (StatsManager.TryGetStats("scene", out sceneStats))
365 {
366 foreach (KeyValuePair<string, Dictionary<string, Stat>> kvp in sceneStats)
367 { 367 {
368 sb.AppendFormat("Slow frames ({0}): {1}\n", stat.Container, stat.Value); 368 foreach (Stat stat in kvp.Value.Values)
369 {
370 if (stat.Verbosity == StatVerbosity.Info)
371 {
372 sb.AppendFormat("{0} ({1}): {2}{3}\n", stat.Name, stat.Container, stat.Value, stat.UnitName);
373 }
374 }
369 } 375 }
370 } 376 }
371 377
diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs
index b5dc24f..31989e5 100644
--- a/OpenSim/Framework/Monitoring/StatsManager.cs
+++ b/OpenSim/Framework/Monitoring/StatsManager.cs
@@ -35,13 +35,23 @@ namespace OpenSim.Framework.Monitoring
35 /// </summary> 35 /// </summary>
36 public class StatsManager 36 public class StatsManager
37 { 37 {
38 // Subcommand used to list other stats.
39 public const string AllSubCommand = "all";
40
41 // Subcommand used to list other stats.
42 public const string ListSubCommand = "list";
43
44 // All subcommands
45 public static HashSet<string> SubCommands = new HashSet<string> { AllSubCommand, ListSubCommand };
46
38 /// <summary> 47 /// <summary>
39 /// Registered stats. 48 /// Registered stats categorized by category/container/shortname
40 /// </summary> 49 /// </summary>
41 /// <remarks> 50 /// <remarks>
42 /// Do not add or remove from this dictionary. 51 /// Do not add or remove directly from this dictionary.
43 /// </remarks> 52 /// </remarks>
44 public static Dictionary<string, Stat> RegisteredStats = new Dictionary<string, Stat>(); 53 public static Dictionary<string, Dictionary<string, Dictionary<string, Stat>>> RegisteredStats
54 = new Dictionary<string, Dictionary<string, Dictionary<string, Stat>>>();
45 55
46 private static AssetStatsCollector assetStats; 56 private static AssetStatsCollector assetStats;
47 private static UserStatsCollector userStats; 57 private static UserStatsCollector userStats;
@@ -51,6 +61,75 @@ namespace OpenSim.Framework.Monitoring
51 public static UserStatsCollector UserStats { get { return userStats; } } 61 public static UserStatsCollector UserStats { get { return userStats; } }
52 public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } 62 public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } }
53 63
64 public static void RegisterConsoleCommands(ICommandConsole console)
65 {
66 console.Commands.AddCommand(
67 "General",
68 false,
69 "show stats",
70 "show stats [list|all|<category>]",
71 "Show statistical information for this server",
72 "If no final argument is specified then legacy statistics information is currently shown.\n"
73 + "If list is specified then statistic categories are shown.\n"
74 + "If all is specified then all registered statistics are shown.\n"
75 + "If a category name is specified then only statistics from that category are shown.\n"
76 + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS",
77 HandleShowStatsCommand);
78 }
79
80 public static void HandleShowStatsCommand(string module, string[] cmd)
81 {
82 ICommandConsole con = MainConsole.Instance;
83
84 if (cmd.Length > 2)
85 {
86 var categoryName = cmd[2];
87
88 if (categoryName == AllSubCommand)
89 {
90 foreach (var category in RegisteredStats.Values)
91 {
92 OutputCategoryStatsToConsole(con, category);
93 }
94 }
95 else if (categoryName == ListSubCommand)
96 {
97 con.Output("Statistic categories available are:");
98 foreach (string category in RegisteredStats.Keys)
99 con.OutputFormat(" {0}", category);
100 }
101 else
102 {
103 Dictionary<string, Dictionary<string, Stat>> category;
104 if (!RegisteredStats.TryGetValue(categoryName, out category))
105 {
106 con.OutputFormat("No such category as {0}", categoryName);
107 }
108 else
109 {
110 OutputCategoryStatsToConsole(con, category);
111 }
112 }
113 }
114 else
115 {
116 // Legacy
117 con.Output(SimExtraStats.Report());
118 }
119 }
120
121 private static void OutputCategoryStatsToConsole(
122 ICommandConsole con, Dictionary<string, Dictionary<string, Stat>> category)
123 {
124 foreach (var container in category.Values)
125 {
126 foreach (Stat stat in container.Values)
127 {
128 con.Output(stat.ToConsoleString());
129 }
130 }
131 }
132
54 /// <summary> 133 /// <summary>
55 /// Start collecting statistics related to assets. 134 /// Start collecting statistics related to assets.
56 /// Should only be called once. 135 /// Should only be called once.
@@ -73,43 +152,100 @@ namespace OpenSim.Framework.Monitoring
73 return userStats; 152 return userStats;
74 } 153 }
75 154
155 /// <summary>
156 /// Registers a statistic.
157 /// </summary>
158 /// <param name='stat'></param>
159 /// <returns></returns>
76 public static bool RegisterStat(Stat stat) 160 public static bool RegisterStat(Stat stat)
77 { 161 {
162 Dictionary<string, Dictionary<string, Stat>> category = null, newCategory;
163 Dictionary<string, Stat> container = null, newContainer;
164
78 lock (RegisteredStats) 165 lock (RegisteredStats)
79 { 166 {
80 if (RegisteredStats.ContainsKey(stat.UniqueName)) 167 // Stat name is not unique across category/container/shortname key.
81 { 168 // XXX: For now just return false. This is to avoid problems in regression tests where all tests
82 // XXX: For now just return false. This is to avoid problems in regression tests where all tests 169 // in a class are run in the same instance of the VM.
83 // in a class are run in the same instance of the VM. 170 if (TryGetStat(stat, out category, out container))
84 return false; 171 return false;
85 172
86// throw new Exception( 173 // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed.
87// "StatsManager already contains stat with ShortName {0} in Category {1}", stat.ShortName, stat.Category); 174 // This means that we don't need to lock or copy them on iteration, which will be a much more
88 } 175 // common operation after startup.
176 if (container != null)
177 newContainer = new Dictionary<string, Stat>(container);
178 else
179 newContainer = new Dictionary<string, Stat>();
89 180
90 // We take a replace-on-write approach here so that we don't need to generate a new Dictionary 181 if (category != null)
91 Dictionary<string, Stat> newRegisteredStats = new Dictionary<string, Stat>(RegisteredStats); 182 newCategory = new Dictionary<string, Dictionary<string, Stat>>(category);
92 newRegisteredStats[stat.UniqueName] = stat; 183 else
93 RegisteredStats = newRegisteredStats; 184 newCategory = new Dictionary<string, Dictionary<string, Stat>>();
185
186 newContainer[stat.ShortName] = stat;
187 newCategory[stat.Container] = newContainer;
188 RegisteredStats[stat.Category] = newCategory;
94 } 189 }
95 190
96 return true; 191 return true;
97 } 192 }
98 193
194 /// <summary>
195 /// Deregister a statistic
196 /// </summary>>
197 /// <param name='stat'></param>
198 /// <returns></returns
99 public static bool DeregisterStat(Stat stat) 199 public static bool DeregisterStat(Stat stat)
100 { 200 {
201 Dictionary<string, Dictionary<string, Stat>> category = null, newCategory;
202 Dictionary<string, Stat> container = null, newContainer;
203
101 lock (RegisteredStats) 204 lock (RegisteredStats)
102 { 205 {
103 if (!RegisteredStats.ContainsKey(stat.UniqueName)) 206 if (!TryGetStat(stat, out category, out container))
104 return false; 207 return false;
105 208
106 Dictionary<string, Stat> newRegisteredStats = new Dictionary<string, Stat>(RegisteredStats); 209 newContainer = new Dictionary<string, Stat>(container);
107 newRegisteredStats.Remove(stat.UniqueName); 210 newContainer.Remove(stat.UniqueName);
108 RegisteredStats = newRegisteredStats; 211
212 newCategory = new Dictionary<string, Dictionary<string, Stat>>(category);
213 newCategory.Remove(stat.Container);
214
215 newCategory[stat.Container] = newContainer;
216 RegisteredStats[stat.Category] = newCategory;
109 217
110 return true; 218 return true;
111 } 219 }
112 } 220 }
221
222 public static bool TryGetStats(string category, out Dictionary<string, Dictionary<string, Stat>> stats)
223 {
224 return RegisteredStats.TryGetValue(category, out stats);
225 }
226
227 public static bool TryGetStat(
228 Stat stat,
229 out Dictionary<string, Dictionary<string, Stat>> category,
230 out Dictionary<string, Stat> container)
231 {
232 category = null;
233 container = null;
234
235 lock (RegisteredStats)
236 {
237 if (RegisteredStats.TryGetValue(stat.Category, out category))
238 {
239 if (category.TryGetValue(stat.Container, out container))
240 {
241 if (container.ContainsKey(stat.ShortName))
242 return true;
243 }
244 }
245 }
246
247 return false;
248 }
113 } 249 }
114 250
115 /// <summary> 251 /// <summary>
@@ -157,9 +293,26 @@ namespace OpenSim.Framework.Monitoring
157 293
158 public virtual double Value { get; set; } 294 public virtual double Value { get; set; }
159 295
296 /// <summary>
297 /// Constructor
298 /// </summary>
299 /// <param name='shortName'>Short name for the stat. Must not contain spaces. e.g. "LongFrames"</param>
300 /// <param name='name'>Human readable name for the stat. e.g. "Long frames"</param>
301 /// <param name='unitName'>
302 /// Unit name for the stat. Should be preceeded by a space if the unit name isn't normally appeneded immediately to the value.
303 /// e.g. " frames"
304 /// </param>
305 /// <param name='category'>Category under which this stat should appear, e.g. "scene". Do not capitalize.</param>
306 /// <param name='container'>Entity to which this stat relates. e.g. scene name if this is a per scene stat.</param>
307 /// <param name='verbosity'>Verbosity of stat. Controls whether it will appear in short stat display or only full display.</param>
308 /// <param name='description'>Description of stat</param>
160 public Stat( 309 public Stat(
161 string shortName, string name, string unitName, string category, string container, StatVerbosity verbosity, string description) 310 string shortName, string name, string unitName, string category, string container, StatVerbosity verbosity, string description)
162 { 311 {
312 if (StatsManager.SubCommands.Contains(category))
313 throw new Exception(
314 string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category));
315
163 ShortName = shortName; 316 ShortName = shortName;
164 Name = name; 317 Name = name;
165 UnitName = unitName; 318 UnitName = unitName;
@@ -175,6 +328,12 @@ namespace OpenSim.Framework.Monitoring
175 { 328 {
176 return string.Format("{0}+{1}+{2}", container, category, shortName); 329 return string.Format("{0}+{1}+{2}", container, category, shortName);
177 } 330 }
331
332 public virtual string ToConsoleString()
333 {
334 return string.Format(
335 "{0}.{1}.{2} : {3}{4}", Category, Container, ShortName, Value, UnitName);
336 }
178 } 337 }
179 338
180 public class PercentageStat : Stat 339 public class PercentageStat : Stat
@@ -192,7 +351,7 @@ namespace OpenSim.Framework.Monitoring
192 if (c == 0) 351 if (c == 0)
193 return 0; 352 return 0;
194 353
195 return (double)Antecedent / c; 354 return (double)Antecedent / c * 100;
196 } 355 }
197 356
198 set 357 set
@@ -203,8 +362,13 @@ namespace OpenSim.Framework.Monitoring
203 362
204 public PercentageStat( 363 public PercentageStat(
205 string shortName, string name, string category, string container, StatVerbosity verbosity, string description) 364 string shortName, string name, string category, string container, StatVerbosity verbosity, string description)
206 : base(shortName, name, " %", category, container, verbosity, description) 365 : base(shortName, name, "%", category, container, verbosity, description) {}
366
367 public override string ToConsoleString()
207 { 368 {
369 return string.Format(
370 "{0}.{1}.{2} : {3:0.##}{4} ({5}/{6})",
371 Category, Container, ShortName, Value, UnitName, Antecedent, Consequent);
208 } 372 }
209 } 373 }
210} \ No newline at end of file 374} \ No newline at end of file
diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs
index 7964f28..a20326d 100644
--- a/OpenSim/Framework/Monitoring/Watchdog.cs
+++ b/OpenSim/Framework/Monitoring/Watchdog.cs
@@ -231,7 +231,25 @@ namespace OpenSim.Framework.Monitoring
231 private static bool RemoveThread(int threadID) 231 private static bool RemoveThread(int threadID)
232 { 232 {
233 lock (m_threads) 233 lock (m_threads)
234 return m_threads.Remove(threadID); 234 {
235 ThreadWatchdogInfo twi;
236 if (m_threads.TryGetValue(threadID, out twi))
237 {
238 m_log.DebugFormat(
239 "[WATCHDOG]: Removing thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId);
240
241 m_threads.Remove(threadID);
242
243 return true;
244 }
245 else
246 {
247 m_log.WarnFormat(
248 "[WATCHDOG]: Requested to remove thread with ID {0} but this is not being monitored", threadID);
249
250 return false;
251 }
252 }
235 } 253 }
236 254
237 public static bool AbortThread(int threadID) 255 public static bool AbortThread(int threadID)
diff --git a/OpenSim/Framework/Pool.cs b/OpenSim/Framework/Pool.cs
new file mode 100644
index 0000000..1ca06c3
--- /dev/null
+++ b/OpenSim/Framework/Pool.cs
@@ -0,0 +1,76 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30
31namespace OpenSim.Framework
32{
33 /// <summary>
34 /// Naive pool implementation.
35 /// </summary>
36 /// <remarks>
37 /// Currently assumes that objects are in a useable state when returned.
38 /// </remarks>
39 public class Pool<T>
40 {
41 private Stack<T> m_pool;
42
43 private int m_maxPoolSize;
44
45 private Func<T> m_createFunction;
46
47 public Pool(Func<T> createFunction, int maxSize)
48 {
49 m_maxPoolSize = maxSize;
50 m_createFunction = createFunction;
51 m_pool = new Stack<T>(m_maxPoolSize);
52 }
53
54 public T GetObject()
55 {
56 lock (m_pool)
57 {
58 if (m_pool.Count > 0)
59 return m_pool.Pop();
60 else
61 return m_createFunction();
62 }
63 }
64
65 public void ReturnObject(T obj)
66 {
67 lock (m_pool)
68 {
69 if (m_pool.Count >= m_maxPoolSize)
70 return;
71 else
72 m_pool.Push(obj);
73 }
74 }
75 }
76} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
index 7a5c16d..aac9c45 100644
--- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs
+++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
@@ -96,11 +96,6 @@ namespace OpenSim.Framework.Servers
96 get { return m_httpServer; } 96 get { return m_httpServer; }
97 } 97 }
98 98
99 /// <summary>
100 /// Holds the non-viewer statistics collection object for this service/server
101 /// </summary>
102 protected IStatsCollector m_stats;
103
104 public BaseOpenSimServer() 99 public BaseOpenSimServer()
105 { 100 {
106 m_startuptime = DateTime.Now; 101 m_startuptime = DateTime.Now;
@@ -177,10 +172,6 @@ namespace OpenSim.Framework.Servers
177 "show info", 172 "show info",
178 "Show general information about the server", HandleShow); 173 "Show general information about the server", HandleShow);
179 174
180 m_console.Commands.AddCommand("General", false, "show stats",
181 "show stats",
182 "Show statistics", HandleShow);
183
184 m_console.Commands.AddCommand("General", false, "show threads", 175 m_console.Commands.AddCommand("General", false, "show threads",
185 "show threads", 176 "show threads",
186 "Show thread status", HandleShow); 177 "Show thread status", HandleShow);
@@ -226,12 +217,7 @@ namespace OpenSim.Framework.Servers
226 { 217 {
227 StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n"); 218 StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n");
228 sb.Append(GetUptimeReport()); 219 sb.Append(GetUptimeReport());
229 220 sb.Append(StatsManager.SimExtraStats.Report());
230 if (m_stats != null)
231 {
232 sb.Append(m_stats.Report());
233 }
234
235 sb.Append(Environment.NewLine); 221 sb.Append(Environment.NewLine);
236 sb.Append(GetThreadsReport()); 222 sb.Append(GetThreadsReport());
237 223
@@ -382,10 +368,6 @@ namespace OpenSim.Framework.Servers
382 { 368 {
383 Notice("set log level [level] - change the console logging level only. For example, off or debug."); 369 Notice("set log level [level] - change the console logging level only. For example, off or debug.");
384 Notice("show info - show server information (e.g. startup path)."); 370 Notice("show info - show server information (e.g. startup path).");
385
386 if (m_stats != null)
387 Notice("show stats - show statistical information for this server");
388
389 Notice("show threads - list tracked threads"); 371 Notice("show threads - list tracked threads");
390 Notice("show uptime - show server startup time and uptime."); 372 Notice("show uptime - show server startup time and uptime.");
391 Notice("show version - show server version."); 373 Notice("show version - show server version.");
@@ -409,11 +391,6 @@ namespace OpenSim.Framework.Servers
409 ShowInfo(); 391 ShowInfo();
410 break; 392 break;
411 393
412 case "stats":
413 if (m_stats != null)
414 Notice(m_stats.Report());
415 break;
416
417 case "threads": 394 case "threads":
418 Notice(GetThreadsReport()); 395 Notice(GetThreadsReport());
419 break; 396 break;
@@ -604,8 +581,7 @@ namespace OpenSim.Framework.Servers
604 581
605 public string osSecret { 582 public string osSecret {
606 // Secret uuid for the simulator 583 // Secret uuid for the simulator
607 get { return m_osSecret; } 584 get { return m_osSecret; }
608
609 } 585 }
610 586
611 public string StatReport(IOSHttpRequest httpRequest) 587 public string StatReport(IOSHttpRequest httpRequest)
@@ -613,11 +589,11 @@ namespace OpenSim.Framework.Servers
613 // If we catch a request for "callback", wrap the response in the value for jsonp 589 // If we catch a request for "callback", wrap the response in the value for jsonp
614 if (httpRequest.Query.ContainsKey("callback")) 590 if (httpRequest.Query.ContainsKey("callback"))
615 { 591 {
616 return httpRequest.Query["callback"].ToString() + "(" + m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");"; 592 return httpRequest.Query["callback"].ToString() + "(" + StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");";
617 } 593 }
618 else 594 else
619 { 595 {
620 return m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version); 596 return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
621 } 597 }
622 } 598 }
623 599
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
index d5bc3c3..b018e57 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -648,7 +648,7 @@ namespace OpenSim.Framework.Servers.HttpServer
648 // Every month or so this will wrap and give bad numbers, not really a problem 648 // Every month or so this will wrap and give bad numbers, not really a problem
649 // since its just for reporting 649 // since its just for reporting
650 int tickdiff = requestEndTick - requestStartTick; 650 int tickdiff = requestEndTick - requestStartTick;
651 if (tickdiff > 3000 && requestHandler.Name != "GetTexture") 651 if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture")
652 { 652 {
653 m_log.InfoFormat( 653 m_log.InfoFormat(
654 "[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms", 654 "[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms",
diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs
index c3c612f..b24641a 100644
--- a/OpenSim/Region/Application/OpenSim.cs
+++ b/OpenSim/Region/Application/OpenSim.cs
@@ -254,8 +254,14 @@ namespace OpenSim
254 m_console.Commands.AddCommand("Debug", false, "debug teleport", "debug teleport", "Toggle teleport route debugging", Debug); 254 m_console.Commands.AddCommand("Debug", false, "debug teleport", "debug teleport", "Toggle teleport route debugging", Debug);
255 255
256 m_console.Commands.AddCommand("Debug", false, "debug scene", 256 m_console.Commands.AddCommand("Debug", false, "debug scene",
257 "debug scene <scripting> <collisions> <physics>", 257 "debug scene active|collisions|physics|scripting|teleport true|false",
258 "Turn on scene debugging", Debug); 258 "Turn on scene debugging.",
259 "If active is false then main scene update and maintenance loops are suspended.\n"
260 + "If collisions is false then collisions with other objects are turned off.\n"
261 + "If physics is false then all physics objects are non-physical.\n"
262 + "If scripting is false then no scripting operations happen.\n"
263 + "If teleport is true then some extra teleport debug information is logged.",
264 Debug);
259 265
260 m_console.Commands.AddCommand("General", false, "change region", 266 m_console.Commands.AddCommand("General", false, "change region",
261 "change region <region name>", 267 "change region <region name>",
@@ -930,7 +936,8 @@ namespace OpenSim
930 } 936 }
931 else 937 else
932 { 938 {
933 MainConsole.Instance.Output("Usage: debug scene scripting|collisions|physics|teleport true|false"); 939 MainConsole.Instance.Output(
940 "Usage: debug scene active|scripting|collisions|physics|teleport true|false");
934 } 941 }
935 942
936 break; 943 break;
diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs
index f784398..4f1b439 100644
--- a/OpenSim/Region/Application/OpenSimBase.cs
+++ b/OpenSim/Region/Application/OpenSimBase.cs
@@ -223,8 +223,6 @@ namespace OpenSim
223 223
224 base.StartupSpecific(); 224 base.StartupSpecific();
225 225
226 m_stats = StatsManager.SimExtraStats;
227
228 // Create a ModuleLoader instance 226 // Create a ModuleLoader instance
229 m_moduleLoader = new ModuleLoader(m_config.Source); 227 m_moduleLoader = new ModuleLoader(m_config.Source);
230 228
@@ -234,51 +232,51 @@ namespace OpenSim
234 plugin.PostInitialise(); 232 plugin.PostInitialise();
235 } 233 }
236 234
237 AddPluginCommands(); 235 if (m_console != null)
236 {
237 StatsManager.RegisterConsoleCommands(m_console);
238 AddPluginCommands(m_console);
239 }
238 } 240 }
239 241
240 protected virtual void AddPluginCommands() 242 protected virtual void AddPluginCommands(CommandConsole console)
241 { 243 {
242 // If console exists add plugin commands. 244 List<string> topics = GetHelpTopics();
243 if (m_console != null)
244 {
245 List<string> topics = GetHelpTopics();
246 245
247 foreach (string topic in topics) 246 foreach (string topic in topics)
248 { 247 {
249 string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1); 248 string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1);
250 249
251 // This is a hack to allow the user to enter the help command in upper or lowercase. This will go 250 // This is a hack to allow the user to enter the help command in upper or lowercase. This will go
252 // away at some point. 251 // away at some point.
253 m_console.Commands.AddCommand(capitalizedTopic, false, "help " + topic, 252 console.Commands.AddCommand(capitalizedTopic, false, "help " + topic,
254 "help " + capitalizedTopic, 253 "help " + capitalizedTopic,
255 "Get help on plugin command '" + topic + "'", 254 "Get help on plugin command '" + topic + "'",
256 HandleCommanderHelp); 255 HandleCommanderHelp);
257 m_console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic, 256 console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic,
258 "help " + capitalizedTopic, 257 "help " + capitalizedTopic,
259 "Get help on plugin command '" + topic + "'", 258 "Get help on plugin command '" + topic + "'",
260 HandleCommanderHelp); 259 HandleCommanderHelp);
261 260
262 ICommander commander = null; 261 ICommander commander = null;
263 262
264 Scene s = SceneManager.CurrentOrFirstScene; 263 Scene s = SceneManager.CurrentOrFirstScene;
265 264
266 if (s != null && s.GetCommanders() != null) 265 if (s != null && s.GetCommanders() != null)
267 { 266 {
268 if (s.GetCommanders().ContainsKey(topic)) 267 if (s.GetCommanders().ContainsKey(topic))
269 commander = s.GetCommanders()[topic]; 268 commander = s.GetCommanders()[topic];
270 } 269 }
271 270
272 if (commander == null) 271 if (commander == null)
273 continue; 272 continue;
274 273
275 foreach (string command in commander.Commands.Keys) 274 foreach (string command in commander.Commands.Keys)
276 { 275 {
277 m_console.Commands.AddCommand(capitalizedTopic, false, 276 console.Commands.AddCommand(capitalizedTopic, false,
278 topic + " " + command, 277 topic + " " + command,
279 topic + " " + commander.Commands[command].ShortHelp(), 278 topic + " " + commander.Commands[command].ShortHelp(),
280 String.Empty, HandleCommanderCommand); 279 String.Empty, HandleCommanderCommand);
281 }
282 } 280 }
283 } 281 }
284 } 282 }
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
index 185f9ce..cc69645 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
@@ -163,8 +163,8 @@ namespace OpenSim.Region.ClientStack.Linden
163 m_HostCapsObj.RegisterHandler( 163 m_HostCapsObj.RegisterHandler(
164 "SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest, "SEED", null)); 164 "SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest, "SEED", null));
165 165
166 m_log.DebugFormat( 166// m_log.DebugFormat(
167 "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID); 167// "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID);
168 168
169 //m_capsHandlers["MapLayer"] = 169 //m_capsHandlers["MapLayer"] =
170 // new LLSDStreamhandler<OSDMapRequest, OSDMapLayerResponse>("POST", 170 // new LLSDStreamhandler<OSDMapRequest, OSDMapLayerResponse>("POST",
@@ -254,11 +254,12 @@ namespace OpenSim.Region.ClientStack.Linden
254 public string SeedCapRequest(string request, string path, string param, 254 public string SeedCapRequest(string request, string path, string param,
255 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) 255 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
256 { 256 {
257// m_log.Debug("[CAPS]: Seed Caps Request in region: " + m_regionName); 257 m_log.DebugFormat(
258 "[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID);
258 259
259 if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint)) 260 if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint))
260 { 261 {
261 m_log.DebugFormat( 262 m_log.WarnFormat(
262 "[CAPS]: Unauthorized CAPS client {0} from {1}", 263 "[CAPS]: Unauthorized CAPS client {0} from {1}",
263 m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint); 264 m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint);
264 265
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
index 594b229..47cb049 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
@@ -94,7 +94,7 @@ namespace OpenSim.Region.ClientStack.Linden
94 94
95 //scene.CommsManager.HttpServer.AddLLSDHandler("/CAPS/EQG/", EventQueueFallBack); 95 //scene.CommsManager.HttpServer.AddLLSDHandler("/CAPS/EQG/", EventQueueFallBack);
96 96
97 scene.EventManager.OnNewClient += OnNewClient; 97// scene.EventManager.OnNewClient += OnNewClient;
98 98
99 // TODO: Leaving these open, or closing them when we 99 // TODO: Leaving these open, or closing them when we
100 // become a child is incorrect. It messes up TP in a big 100 // become a child is incorrect. It messes up TP in a big
@@ -102,6 +102,7 @@ namespace OpenSim.Region.ClientStack.Linden
102 // circuit is there. 102 // circuit is there.
103 103
104 scene.EventManager.OnClientClosed += ClientClosed; 104 scene.EventManager.OnClientClosed += ClientClosed;
105
105 scene.EventManager.OnMakeChildAgent += MakeChildAgent; 106 scene.EventManager.OnMakeChildAgent += MakeChildAgent;
106 scene.EventManager.OnRegisterCaps += OnRegisterCaps; 107 scene.EventManager.OnRegisterCaps += OnRegisterCaps;
107 108
@@ -110,10 +111,10 @@ namespace OpenSim.Region.ClientStack.Linden
110 false, 111 false,
111 "debug eq", 112 "debug eq",
112 "debug eq [0|1|2]", 113 "debug eq [0|1|2]",
113 "Turn on event queue debugging" 114 "Turn on event queue debugging\n"
114 + "<= 0 - turns off all event queue logging" 115 + " <= 0 - turns off all event queue logging\n"
115 + ">= 1 - turns on outgoing event logging" 116 + " >= 1 - turns on outgoing event logging\n"
116 + ">= 2 - turns on poll notification", 117 + " >= 2 - turns on poll notification",
117 HandleDebugEq); 118 HandleDebugEq);
118 } 119 }
119 else 120 else
@@ -226,16 +227,6 @@ namespace OpenSim.Region.ClientStack.Linden
226 227
227 #endregion 228 #endregion
228 229
229 private void OnNewClient(IClientAPI client)
230 {
231 //client.OnLogout += ClientClosed;
232 }
233
234// private void ClientClosed(IClientAPI client)
235// {
236// ClientClosed(client.AgentId);
237// }
238
239 private void ClientClosed(UUID agentID, Scene scene) 230 private void ClientClosed(UUID agentID, Scene scene)
240 { 231 {
241// m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName); 232// m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName);
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs
index 36af55f..17c7270 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs
@@ -107,7 +107,7 @@ namespace OpenSim.Region.ClientStack.Linden
107 107
108 UUID capID = UUID.Random(); 108 UUID capID = UUID.Random();
109 109
110 m_log.DebugFormat("[REGION CONSOLE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); 110// m_log.DebugFormat("[REGION CONSOLE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
111 caps.RegisterHandler( 111 caps.RegisterHandler(
112 "SimConsoleAsync", 112 "SimConsoleAsync",
113 new ConsoleHandler("/CAPS/" + capID + "/", "SimConsoleAsync", agentID, this, m_scene)); 113 new ConsoleHandler("/CAPS/" + capID + "/", "SimConsoleAsync", agentID, this, m_scene));
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs b/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs
index 1b8535c..e22670b 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs
@@ -45,7 +45,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
45 public Packet Packet; 45 public Packet Packet;
46 46
47 /// <summary> 47 /// <summary>
48 /// Default constructor 48 /// No arg constructor.
49 /// </summary>
50 public IncomingPacket() {}
51
52 /// <summary>
53 /// Constructor
49 /// </summary> 54 /// </summary>
50 /// <param name="client">Reference to the client this packet came from</param> 55 /// <param name="client">Reference to the client this packet came from</param>
51 /// <param name="packet">Packet data</param> 56 /// <param name="packet">Packet data</param>
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index 62f51d9..7427c59 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -347,7 +347,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
347 private int m_moneyBalance; 347 private int m_moneyBalance;
348 private int m_animationSequenceNumber = 1; 348 private int m_animationSequenceNumber = 1;
349 private bool m_SendLogoutPacketWhenClosing = true; 349 private bool m_SendLogoutPacketWhenClosing = true;
350 private AgentUpdateArgs lastarg; 350
351 /// <summary>
352 /// We retain a single AgentUpdateArgs so that we can constantly reuse it rather than construct a new one for
353 /// every single incoming AgentUpdate. Every client sends 10 AgentUpdate UDP messages per second, even if it
354 /// is doing absolutely nothing.
355 /// </summary>
356 /// <remarks>
357 /// This does mean that agent updates must be processed synchronously, at least for each client, and called methods
358 /// cannot retain a reference to it outside of that method.
359 /// </remarks>
360 private AgentUpdateArgs m_lastAgentUpdateArgs;
351 361
352 protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>(); 362 protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>();
353 protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers 363 protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers
@@ -3922,7 +3932,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3922 { 3932 {
3923 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value; 3933 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value;
3924 3934
3925 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); 3935 ImprovedTerseObjectUpdatePacket packet
3936 = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
3937
3926 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 3938 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3927 packet.RegionData.TimeDilation = timeDilation; 3939 packet.RegionData.TimeDilation = timeDilation;
3928 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; 3940 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
@@ -3967,7 +3979,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3967 { 3979 {
3968 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value; 3980 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;
3969 3981
3970 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); 3982 ImprovedTerseObjectUpdatePacket packet
3983 = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
3984 PacketType.ImprovedTerseObjectUpdate);
3985
3971 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 3986 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3972 packet.RegionData.TimeDilation = timeDilation; 3987 packet.RegionData.TimeDilation = timeDilation;
3973 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; 3988 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
@@ -4959,7 +4974,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4959 Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Y, -64.0f, 64.0f), data, pos); pos += 2; 4974 Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Y, -64.0f, 64.0f), data, pos); pos += 2;
4960 Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Z, -64.0f, 64.0f), data, pos); pos += 2; 4975 Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Z, -64.0f, 64.0f), data, pos); pos += 2;
4961 4976
4962 ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock(); 4977 ImprovedTerseObjectUpdatePacket.ObjectDataBlock block
4978 = PacketPool.Instance.GetDataBlock<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>();
4979
4963 block.Data = data; 4980 block.Data = data;
4964 4981
4965 if (textureEntry != null && textureEntry.Length > 0) 4982 if (textureEntry != null && textureEntry.Length > 0)
@@ -5191,7 +5208,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5191 protected virtual void RegisterLocalPacketHandlers() 5208 protected virtual void RegisterLocalPacketHandlers()
5192 { 5209 {
5193 AddLocalPacketHandler(PacketType.LogoutRequest, HandleLogout); 5210 AddLocalPacketHandler(PacketType.LogoutRequest, HandleLogout);
5211
5212 // If AgentUpdate is ever handled asynchronously, then we will also need to construct a new AgentUpdateArgs
5213 // for each AgentUpdate packet.
5194 AddLocalPacketHandler(PacketType.AgentUpdate, HandleAgentUpdate, false); 5214 AddLocalPacketHandler(PacketType.AgentUpdate, HandleAgentUpdate, false);
5215
5195 AddLocalPacketHandler(PacketType.ViewerEffect, HandleViewerEffect, false); 5216 AddLocalPacketHandler(PacketType.ViewerEffect, HandleViewerEffect, false);
5196 AddLocalPacketHandler(PacketType.AgentCachedTexture, HandleAgentTextureCached, false); 5217 AddLocalPacketHandler(PacketType.AgentCachedTexture, HandleAgentTextureCached, false);
5197 AddLocalPacketHandler(PacketType.MultipleObjectUpdate, HandleMultipleObjUpdate, false); 5218 AddLocalPacketHandler(PacketType.MultipleObjectUpdate, HandleMultipleObjUpdate, false);
@@ -5418,80 +5439,83 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5418 5439
5419 #region Scene/Avatar 5440 #region Scene/Avatar
5420 5441
5421 private bool HandleAgentUpdate(IClientAPI sener, Packet Pack) 5442 private bool HandleAgentUpdate(IClientAPI sener, Packet packet)
5422 { 5443 {
5423 if (OnAgentUpdate != null) 5444 if (OnAgentUpdate != null)
5424 { 5445 {
5425 bool update = false; 5446 AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet;
5426 AgentUpdatePacket agenUpdate = (AgentUpdatePacket)Pack;
5427 5447
5428 #region Packet Session and User Check 5448 #region Packet Session and User Check
5429 if (agenUpdate.AgentData.SessionID != SessionId || agenUpdate.AgentData.AgentID != AgentId) 5449 if (agentUpdate.AgentData.SessionID != SessionId || agentUpdate.AgentData.AgentID != AgentId)
5450 {
5451 PacketPool.Instance.ReturnPacket(packet);
5430 return false; 5452 return false;
5453 }
5431 #endregion 5454 #endregion
5432 5455
5433 AgentUpdatePacket.AgentDataBlock x = agenUpdate.AgentData; 5456 bool update = false;
5434 5457 AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData;
5435 // We can only check when we have something to check
5436 // against.
5437 5458
5438 if (lastarg != null) 5459 if (m_lastAgentUpdateArgs != null)
5439 { 5460 {
5461 // These should be ordered from most-likely to
5462 // least likely to change. I've made an initial
5463 // guess at that.
5440 update = 5464 update =
5441 ( 5465 (
5442 (x.BodyRotation != lastarg.BodyRotation) || 5466 (x.BodyRotation != m_lastAgentUpdateArgs.BodyRotation) ||
5443 (x.CameraAtAxis != lastarg.CameraAtAxis) || 5467 (x.CameraAtAxis != m_lastAgentUpdateArgs.CameraAtAxis) ||
5444 (x.CameraCenter != lastarg.CameraCenter) || 5468 (x.CameraCenter != m_lastAgentUpdateArgs.CameraCenter) ||
5445 (x.CameraLeftAxis != lastarg.CameraLeftAxis) || 5469 (x.CameraLeftAxis != m_lastAgentUpdateArgs.CameraLeftAxis) ||
5446 (x.CameraUpAxis != lastarg.CameraUpAxis) || 5470 (x.CameraUpAxis != m_lastAgentUpdateArgs.CameraUpAxis) ||
5447 (x.ControlFlags != lastarg.ControlFlags) || 5471 (x.ControlFlags != m_lastAgentUpdateArgs.ControlFlags) ||
5448 (x.Far != lastarg.Far) || 5472 (x.Far != m_lastAgentUpdateArgs.Far) ||
5449 (x.Flags != lastarg.Flags) || 5473 (x.Flags != m_lastAgentUpdateArgs.Flags) ||
5450 (x.State != lastarg.State) || 5474 (x.State != m_lastAgentUpdateArgs.State) ||
5451 (x.HeadRotation != lastarg.HeadRotation) || 5475 (x.HeadRotation != m_lastAgentUpdateArgs.HeadRotation) ||
5452 (x.SessionID != lastarg.SessionID) || 5476 (x.SessionID != m_lastAgentUpdateArgs.SessionID) ||
5453 (x.AgentID != lastarg.AgentID) 5477 (x.AgentID != m_lastAgentUpdateArgs.AgentID)
5454 ); 5478 );
5455 } 5479 }
5456 else 5480 else
5457 { 5481 {
5482 m_lastAgentUpdateArgs = new AgentUpdateArgs();
5458 update = true; 5483 update = true;
5459 } 5484 }
5460 5485
5461 // These should be ordered from most-likely to
5462 // least likely to change. I've made an initial
5463 // guess at that.
5464
5465 if (update) 5486 if (update)
5466 { 5487 {
5467// m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name); 5488// m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name);
5468 5489
5469 AgentUpdateArgs arg = new AgentUpdateArgs(); 5490 m_lastAgentUpdateArgs.AgentID = x.AgentID;
5470 arg.AgentID = x.AgentID; 5491 m_lastAgentUpdateArgs.BodyRotation = x.BodyRotation;
5471 arg.BodyRotation = x.BodyRotation; 5492 m_lastAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis;
5472 arg.CameraAtAxis = x.CameraAtAxis; 5493 m_lastAgentUpdateArgs.CameraCenter = x.CameraCenter;
5473 arg.CameraCenter = x.CameraCenter; 5494 m_lastAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis;
5474 arg.CameraLeftAxis = x.CameraLeftAxis; 5495 m_lastAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis;
5475 arg.CameraUpAxis = x.CameraUpAxis; 5496 m_lastAgentUpdateArgs.ControlFlags = x.ControlFlags;
5476 arg.ControlFlags = x.ControlFlags; 5497 m_lastAgentUpdateArgs.Far = x.Far;
5477 arg.Far = x.Far; 5498 m_lastAgentUpdateArgs.Flags = x.Flags;
5478 arg.Flags = x.Flags; 5499 m_lastAgentUpdateArgs.HeadRotation = x.HeadRotation;
5479 arg.HeadRotation = x.HeadRotation; 5500 m_lastAgentUpdateArgs.SessionID = x.SessionID;
5480 arg.SessionID = x.SessionID; 5501 m_lastAgentUpdateArgs.State = x.State;
5481 arg.State = x.State; 5502
5482 UpdateAgent handlerAgentUpdate = OnAgentUpdate; 5503 UpdateAgent handlerAgentUpdate = OnAgentUpdate;
5483 UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; 5504 UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate;
5484 lastarg = arg; // save this set of arguments for nexttime 5505
5485 if (handlerPreAgentUpdate != null) 5506 if (handlerPreAgentUpdate != null)
5486 OnPreAgentUpdate(this, arg); 5507 OnPreAgentUpdate(this, m_lastAgentUpdateArgs);
5508
5487 if (handlerAgentUpdate != null) 5509 if (handlerAgentUpdate != null)
5488 OnAgentUpdate(this, arg); 5510 OnAgentUpdate(this, m_lastAgentUpdateArgs);
5489 5511
5490 handlerAgentUpdate = null; 5512 handlerAgentUpdate = null;
5491 handlerPreAgentUpdate = null; 5513 handlerPreAgentUpdate = null;
5492 } 5514 }
5493 } 5515 }
5494 5516
5517 PacketPool.Instance.ReturnPacket(packet);
5518
5495 return true; 5519 return true;
5496 } 5520 }
5497 5521
@@ -9056,7 +9080,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
9056 } 9080 }
9057 #endregion 9081 #endregion
9058 9082
9059 switch (Utils.BytesToString(messagePacket.MethodData.Method)) 9083 string method = Utils.BytesToString(messagePacket.MethodData.Method);
9084
9085 switch (method)
9060 { 9086 {
9061 case "getinfo": 9087 case "getinfo":
9062 if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false)) 9088 if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false))
@@ -9372,7 +9398,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
9372 return true; 9398 return true;
9373 9399
9374 default: 9400 default:
9375 m_log.Error("EstateOwnerMessage: Unknown method requested\n" + messagePacket); 9401 m_log.WarnFormat(
9402 "[LLCLIENTVIEW]: EstateOwnerMessage: Unknown method {0} requested for {1} in {2}",
9403 method, Name, Scene.Name);
9404
9405 for (int i = 0; i < messagePacket.ParamList.Length; i++)
9406 {
9407 EstateOwnerMessagePacket.ParamListBlock block = messagePacket.ParamList[i];
9408 string data = (string)Utils.BytesToString(block.Parameter);
9409 m_log.DebugFormat("[LLCLIENTVIEW]: Param {0}={1}", i, data);
9410 }
9411
9376 return true; 9412 return true;
9377 } 9413 }
9378 9414
@@ -11758,7 +11794,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11758 logPacket = false; 11794 logPacket = false;
11759 11795
11760 if (DebugPacketLevel <= 50 11796 if (DebugPacketLevel <= 50
11761 & (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate)) 11797 && (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate))
11762 logPacket = false; 11798 logPacket = false;
11763 11799
11764 if (DebugPacketLevel <= 25 && packet.Type == PacketType.ObjectPropertiesFamily) 11800 if (DebugPacketLevel <= 25 && packet.Type == PacketType.ObjectPropertiesFamily)
@@ -11832,8 +11868,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11832 11868
11833 if (!ProcessPacketMethod(packet)) 11869 if (!ProcessPacketMethod(packet))
11834 m_log.Warn("[CLIENT]: unhandled packet " + packet.Type); 11870 m_log.Warn("[CLIENT]: unhandled packet " + packet.Type);
11835
11836 PacketPool.Instance.ReturnPacket(packet);
11837 } 11871 }
11838 11872
11839 private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket) 11873 private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket)
@@ -12286,7 +12320,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
12286 ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f); 12320 ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f);
12287 12321
12288 12322
12289 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); 12323 ImprovedTerseObjectUpdatePacket packet
12324 = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
12325 PacketType.ImprovedTerseObjectUpdate);
12326
12290 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 12327 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
12291 packet.RegionData.TimeDilation = timeDilation; 12328 packet.RegionData.TimeDilation = timeDilation;
12292 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1]; 12329 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
index d11fcbf..419de66 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
@@ -37,6 +37,7 @@ using log4net;
37using Nini.Config; 37using Nini.Config;
38using OpenMetaverse.Packets; 38using OpenMetaverse.Packets;
39using OpenSim.Framework; 39using OpenSim.Framework;
40using OpenSim.Framework.Console;
40using OpenSim.Framework.Monitoring; 41using OpenSim.Framework.Monitoring;
41using OpenSim.Region.Framework.Scenes; 42using OpenSim.Region.Framework.Scenes;
42using OpenMetaverse; 43using OpenMetaverse;
@@ -100,9 +101,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
100 101
101 /// <summary>The measured resolution of Environment.TickCount</summary> 102 /// <summary>The measured resolution of Environment.TickCount</summary>
102 public readonly float TickCountResolution; 103 public readonly float TickCountResolution;
104
103 /// <summary>Number of prim updates to put on the queue each time the 105 /// <summary>Number of prim updates to put on the queue each time the
104 /// OnQueueEmpty event is triggered for updates</summary> 106 /// OnQueueEmpty event is triggered for updates</summary>
105 public readonly int PrimUpdatesPerCallback; 107 public readonly int PrimUpdatesPerCallback;
108
106 /// <summary>Number of texture packets to put on the queue each time the 109 /// <summary>Number of texture packets to put on the queue each time the
107 /// OnQueueEmpty event is triggered for textures</summary> 110 /// OnQueueEmpty event is triggered for textures</summary>
108 public readonly int TextureSendLimit; 111 public readonly int TextureSendLimit;
@@ -111,6 +114,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
111 //PacketEventDictionary packetEvents = new PacketEventDictionary(); 114 //PacketEventDictionary packetEvents = new PacketEventDictionary();
112 /// <summary>Incoming packets that are awaiting handling</summary> 115 /// <summary>Incoming packets that are awaiting handling</summary>
113 private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>(); 116 private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
117
114 /// <summary></summary> 118 /// <summary></summary>
115 //private UDPClientCollection m_clients = new UDPClientCollection(); 119 //private UDPClientCollection m_clients = new UDPClientCollection();
116 /// <summary>Bandwidth throttle for this UDP server</summary> 120 /// <summary>Bandwidth throttle for this UDP server</summary>
@@ -121,28 +125,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP
121 125
122 /// <summary>Manages authentication for agent circuits</summary> 126 /// <summary>Manages authentication for agent circuits</summary>
123 private AgentCircuitManager m_circuitManager; 127 private AgentCircuitManager m_circuitManager;
128
124 /// <summary>Reference to the scene this UDP server is attached to</summary> 129 /// <summary>Reference to the scene this UDP server is attached to</summary>
125 protected Scene m_scene; 130 protected Scene m_scene;
131
126 /// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary> 132 /// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary>
127 private Location m_location; 133 private Location m_location;
134
128 /// <summary>The size of the receive buffer for the UDP socket. This value 135 /// <summary>The size of the receive buffer for the UDP socket. This value
129 /// is passed up to the operating system and used in the system networking 136 /// is passed up to the operating system and used in the system networking
130 /// stack. Use zero to leave this value as the default</summary> 137 /// stack. Use zero to leave this value as the default</summary>
131 private int m_recvBufferSize; 138 private int m_recvBufferSize;
139
132 /// <summary>Flag to process packets asynchronously or synchronously</summary> 140 /// <summary>Flag to process packets asynchronously or synchronously</summary>
133 private bool m_asyncPacketHandling; 141 private bool m_asyncPacketHandling;
142
134 /// <summary>Tracks whether or not a packet was sent each round so we know 143 /// <summary>Tracks whether or not a packet was sent each round so we know
135 /// whether or not to sleep</summary> 144 /// whether or not to sleep</summary>
136 private bool m_packetSent; 145 private bool m_packetSent;
137 146
138 /// <summary>Environment.TickCount of the last time that packet stats were reported to the scene</summary> 147 /// <summary>Environment.TickCount of the last time that packet stats were reported to the scene</summary>
139 private int m_elapsedMSSinceLastStatReport = 0; 148 private int m_elapsedMSSinceLastStatReport = 0;
149
140 /// <summary>Environment.TickCount of the last time the outgoing packet handler executed</summary> 150 /// <summary>Environment.TickCount of the last time the outgoing packet handler executed</summary>
141 private int m_tickLastOutgoingPacketHandler; 151 private int m_tickLastOutgoingPacketHandler;
152
142 /// <summary>Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped</summary> 153 /// <summary>Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped</summary>
143 private int m_elapsedMSOutgoingPacketHandler; 154 private int m_elapsedMSOutgoingPacketHandler;
155
144 /// <summary>Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed</summary> 156 /// <summary>Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed</summary>
145 private int m_elapsed100MSOutgoingPacketHandler; 157 private int m_elapsed100MSOutgoingPacketHandler;
158
146 /// <summary>Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed</summary> 159 /// <summary>Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed</summary>
147 private int m_elapsed500MSOutgoingPacketHandler; 160 private int m_elapsed500MSOutgoingPacketHandler;
148 161
@@ -155,6 +168,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
155 /// <summary>Flag to signal when clients should send pings</summary> 168 /// <summary>Flag to signal when clients should send pings</summary>
156 protected bool m_sendPing; 169 protected bool m_sendPing;
157 170
171 private Pool<IncomingPacket> m_incomingPacketPool;
172
158 private int m_defaultRTO = 0; 173 private int m_defaultRTO = 0;
159 private int m_maxRTO = 0; 174 private int m_maxRTO = 0;
160 private int m_ackTimeout = 0; 175 private int m_ackTimeout = 0;
@@ -175,7 +190,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
175 /// </summary> 190 /// </summary>
176 private IClientAPI m_currentIncomingClient; 191 private IClientAPI m_currentIncomingClient;
177 192
178 public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) 193 public LLUDPServer(
194 IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port,
195 IConfigSource configSource, AgentCircuitManager circuitManager)
179 : base(listenIP, (int)port) 196 : base(listenIP, (int)port)
180 { 197 {
181 #region Environment.TickCount Measurement 198 #region Environment.TickCount Measurement
@@ -229,6 +246,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
229 { 246 {
230 PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true); 247 PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true);
231 PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true); 248 PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true);
249 UsePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", false);
232 } 250 }
233 251
234 #region BinaryStats 252 #region BinaryStats
@@ -258,20 +276,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
258 276
259 m_throttle = new TokenBucket(null, sceneThrottleBps); 277 m_throttle = new TokenBucket(null, sceneThrottleBps);
260 ThrottleRates = new ThrottleRates(configSource); 278 ThrottleRates = new ThrottleRates(configSource);
279
280 if (UsePools)
281 m_incomingPacketPool = new Pool<IncomingPacket>(() => new IncomingPacket(), 500);
261 } 282 }
262 283
263 public void Start() 284 public void Start()
264 { 285 {
265 if (m_scene == null) 286 StartInbound();
266 throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); 287 StartOutbound();
288
289 m_elapsedMSSinceLastStatReport = Environment.TickCount;
290 }
267 291
292 private void StartInbound()
293 {
268 m_log.InfoFormat( 294 m_log.InfoFormat(
269 "[LLUDPSERVER]: Starting the LLUDP server in {0} mode", 295 "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
270 m_asyncPacketHandling ? "asynchronous" : "synchronous"); 296 m_asyncPacketHandling ? "asynchronous" : "synchronous", UsePools);
271 297
272 base.Start(m_recvBufferSize, m_asyncPacketHandling); 298 base.StartInbound(m_recvBufferSize, m_asyncPacketHandling);
273 299
274 // Start the packet processing threads 300 // This thread will process the packets received that are placed on the packetInbox
275 Watchdog.StartThread( 301 Watchdog.StartThread(
276 IncomingPacketHandler, 302 IncomingPacketHandler,
277 string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName), 303 string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName),
@@ -280,6 +306,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
280 true, 306 true,
281 GetWatchdogIncomingAlarmData, 307 GetWatchdogIncomingAlarmData,
282 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS); 308 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
309 }
310
311 private new void StartOutbound()
312 {
313 m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server");
314
315 base.StartOutbound();
283 316
284 Watchdog.StartThread( 317 Watchdog.StartThread(
285 OutgoingPacketHandler, 318 OutgoingPacketHandler,
@@ -289,8 +322,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
289 true, 322 true,
290 GetWatchdogOutgoingAlarmData, 323 GetWatchdogOutgoingAlarmData,
291 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS); 324 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
325 }
292 326
293 m_elapsedMSSinceLastStatReport = Environment.TickCount; 327 public void Stop()
328 {
329 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
330 base.StopOutbound();
331 base.StopInbound();
294 } 332 }
295 333
296 /// <summary> 334 /// <summary>
@@ -315,12 +353,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
315 m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none"); 353 m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none");
316 } 354 }
317 355
318 public new void Stop()
319 {
320 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
321 base.Stop();
322 }
323
324 public void AddScene(IScene scene) 356 public void AddScene(IScene scene)
325 { 357 {
326 if (m_scene != null) 358 if (m_scene != null)
@@ -337,6 +369,81 @@ namespace OpenSim.Region.ClientStack.LindenUDP
337 369
338 m_scene = (Scene)scene; 370 m_scene = (Scene)scene;
339 m_location = new Location(m_scene.RegionInfo.RegionHandle); 371 m_location = new Location(m_scene.RegionInfo.RegionHandle);
372
373 MainConsole.Instance.Commands.AddCommand(
374 "Debug",
375 false,
376 "debug lludp start",
377 "debug lludp start <in|out|all>",
378 "Control LLUDP packet processing.",
379 "No effect if packet processing has already started.\n"
380 + "in - start inbound processing.\n"
381 + "out - start outbound processing.\n"
382 + "all - start in and outbound processing.\n",
383 HandleStartCommand);
384
385 MainConsole.Instance.Commands.AddCommand(
386 "Debug",
387 false,
388 "debug lludp stop",
389 "debug lludp stop <in|out|all>",
390 "Stop LLUDP packet processing.",
391 "No effect if packet processing has already stopped.\n"
392 + "in - stop inbound processing.\n"
393 + "out - stop outbound processing.\n"
394 + "all - stop in and outbound processing.\n",
395 HandleStopCommand);
396
397 MainConsole.Instance.Commands.AddCommand(
398 "Debug",
399 false,
400 "debug lludp status",
401 "debug lludp status",
402 "Return status of LLUDP packet processing.",
403 HandleStatusCommand);
404 }
405
406 private void HandleStartCommand(string module, string[] args)
407 {
408 if (args.Length != 4)
409 {
410 MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>");
411 return;
412 }
413
414 string subCommand = args[3];
415
416 if (subCommand == "in" || subCommand == "all")
417 StartInbound();
418
419 if (subCommand == "out" || subCommand == "all")
420 StartOutbound();
421 }
422
423 private void HandleStopCommand(string module, string[] args)
424 {
425 if (args.Length != 4)
426 {
427 MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>");
428 return;
429 }
430
431 string subCommand = args[3];
432
433 if (subCommand == "in" || subCommand == "all")
434 StopInbound();
435
436 if (subCommand == "out" || subCommand == "all")
437 StopOutbound();
438 }
439
440 private void HandleStatusCommand(string module, string[] args)
441 {
442 MainConsole.Instance.OutputFormat(
443 "IN LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningInbound ? "enabled" : "disabled");
444
445 MainConsole.Instance.OutputFormat(
446 "OUT LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningOutbound ? "enabled" : "disabled");
340 } 447 }
341 448
342 public bool HandlesRegion(Location x) 449 public bool HandlesRegion(Location x)
@@ -420,6 +527,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
420 byte[] data = packet.ToBytes(); 527 byte[] data = packet.ToBytes();
421 SendPacketData(udpClient, data, packet.Type, category, method); 528 SendPacketData(udpClient, data, packet.Type, category, method);
422 } 529 }
530
531 PacketPool.Instance.ReturnPacket(packet);
423 } 532 }
424 533
425 /// <summary> 534 /// <summary>
@@ -704,7 +813,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
704 LLUDPClient udpClient = null; 813 LLUDPClient udpClient = null;
705 Packet packet = null; 814 Packet packet = null;
706 int packetEnd = buffer.DataLength - 1; 815 int packetEnd = buffer.DataLength - 1;
707 IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; 816 IPEndPoint endPoint = (IPEndPoint)buffer.RemoteEndPoint;
708 817
709 #region Decoding 818 #region Decoding
710 819
@@ -714,7 +823,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
714// "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}", 823// "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}",
715// buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); 824// buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
716 825
717 return; // Drop undersizd packet 826 return; // Drop undersized packet
718 } 827 }
719 828
720 int headerLen = 7; 829 int headerLen = 7;
@@ -737,7 +846,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
737 846
738 try 847 try
739 { 848 {
740 packet = Packet.BuildPacket(buffer.Data, ref packetEnd, 849// packet = Packet.BuildPacket(buffer.Data, ref packetEnd,
850// // Only allocate a buffer for zerodecoding if the packet is zerocoded
851// ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
852 // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we
853 // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all
854 // bytes are copied out).
855 packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd,
741 // Only allocate a buffer for zerodecoding if the packet is zerocoded 856 // Only allocate a buffer for zerodecoding if the packet is zerocoded
742 ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); 857 ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
743 } 858 }
@@ -752,11 +867,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
752 867
753 return; // Drop short packet 868 return; // Drop short packet
754 } 869 }
755 catch(Exception e) 870 catch (Exception e)
756 { 871 {
757 if (m_malformedCount < 100) 872 if (m_malformedCount < 100)
758 m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString()); 873 m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString());
874
759 m_malformedCount++; 875 m_malformedCount++;
876
760 if ((m_malformedCount % 100000) == 0) 877 if ((m_malformedCount % 100000) == 0)
761 m_log.DebugFormat("[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack.", m_malformedCount); 878 m_log.DebugFormat("[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack.", m_malformedCount);
762 } 879 }
@@ -777,7 +894,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
777 // UseCircuitCode handling 894 // UseCircuitCode handling
778 if (packet.Type == PacketType.UseCircuitCode) 895 if (packet.Type == PacketType.UseCircuitCode)
779 { 896 {
780 object[] array = new object[] { buffer, packet }; 897 // We need to copy the endpoint so that it doesn't get changed when another thread reuses the
898 // buffer.
899 object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
781 900
782 Util.FireAndForget(HandleUseCircuitCode, array); 901 Util.FireAndForget(HandleUseCircuitCode, array);
783 902
@@ -786,7 +905,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
786 905
787 // Determine which agent this packet came from 906 // Determine which agent this packet came from
788 IClientAPI client; 907 IClientAPI client;
789 if (!m_scene.TryGetClient(address, out client) || !(client is LLClientView)) 908 if (!m_scene.TryGetClient(endPoint, out client) || !(client is LLClientView))
790 { 909 {
791 //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); 910 //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName);
792 return; 911 return;
@@ -810,6 +929,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
810 // Handle appended ACKs 929 // Handle appended ACKs
811 if (packet.Header.AppendedAcks && packet.Header.AckList != null) 930 if (packet.Header.AppendedAcks && packet.Header.AckList != null)
812 { 931 {
932// m_log.DebugFormat(
933// "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}",
934// packet.Header.AckList.Length, client.Name, m_scene.Name);
935
813 for (int i = 0; i < packet.Header.AckList.Length; i++) 936 for (int i = 0; i < packet.Header.AckList.Length; i++)
814 udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent); 937 udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent);
815 } 938 }
@@ -819,6 +942,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
819 { 942 {
820 PacketAckPacket ackPacket = (PacketAckPacket)packet; 943 PacketAckPacket ackPacket = (PacketAckPacket)packet;
821 944
945// m_log.DebugFormat(
946// "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}",
947// ackPacket.Packets.Length, client.Name, m_scene.Name);
948
822 for (int i = 0; i < ackPacket.Packets.Length; i++) 949 for (int i = 0; i < ackPacket.Packets.Length; i++)
823 udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent); 950 udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent);
824 951
@@ -832,6 +959,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
832 959
833 if (packet.Header.Reliable) 960 if (packet.Header.Reliable)
834 { 961 {
962// m_log.DebugFormat(
963// "[LLUDPSERVER]: Adding ack request for {0} {1} from {2} in {3}",
964// packet.Type, packet.Header.Sequence, client.Name, m_scene.Name);
965
835 udpClient.PendingAcks.Enqueue(packet.Header.Sequence); 966 udpClient.PendingAcks.Enqueue(packet.Header.Sequence);
836 967
837 // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, 968 // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out,
@@ -878,6 +1009,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
878 1009
879 if (packet.Type == PacketType.StartPingCheck) 1010 if (packet.Type == PacketType.StartPingCheck)
880 { 1011 {
1012// m_log.DebugFormat("[LLUDPSERVER]: Handling ping from {0} in {1}", client.Name, m_scene.Name);
1013
881 // We don't need to do anything else with ping checks 1014 // We don't need to do anything else with ping checks
882 StartPingCheckPacket startPing = (StartPingCheckPacket)packet; 1015 StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
883 CompletePing(udpClient, startPing.PingID.PingID); 1016 CompletePing(udpClient, startPing.PingID.PingID);
@@ -897,8 +1030,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
897 1030
898 #endregion Ping Check Handling 1031 #endregion Ping Check Handling
899 1032
1033 IncomingPacket incomingPacket;
1034
900 // Inbox insertion 1035 // Inbox insertion
901 packetInbox.Enqueue(new IncomingPacket((LLClientView)client, packet)); 1036 if (UsePools)
1037 {
1038 incomingPacket = m_incomingPacketPool.GetObject();
1039 incomingPacket.Client = (LLClientView)client;
1040 incomingPacket.Packet = packet;
1041 }
1042 else
1043 {
1044 incomingPacket = new IncomingPacket((LLClientView)client, packet);
1045 }
1046
1047 packetInbox.Enqueue(incomingPacket);
902 } 1048 }
903 1049
904 #region BinaryStats 1050 #region BinaryStats
@@ -984,21 +1130,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
984 1130
985 private void HandleUseCircuitCode(object o) 1131 private void HandleUseCircuitCode(object o)
986 { 1132 {
987 IPEndPoint remoteEndPoint = null; 1133 IPEndPoint endPoint = null;
988 IClientAPI client = null; 1134 IClientAPI client = null;
989 1135
990 try 1136 try
991 { 1137 {
992 // DateTime startTime = DateTime.Now; 1138 // DateTime startTime = DateTime.Now;
993 object[] array = (object[])o; 1139 object[] array = (object[])o;
994 UDPPacketBuffer buffer = (UDPPacketBuffer)array[0]; 1140 endPoint = (IPEndPoint)array[0];
995 UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1]; 1141 UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1];
996 1142
997 m_log.DebugFormat( 1143 m_log.DebugFormat(
998 "[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}", 1144 "[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}",
999 uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, buffer.RemoteEndPoint); 1145 uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, endPoint);
1000
1001 remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint;
1002 1146
1003 AuthenticateResponse sessionInfo; 1147 AuthenticateResponse sessionInfo;
1004 if (IsClientAuthorized(uccp, out sessionInfo)) 1148 if (IsClientAuthorized(uccp, out sessionInfo))
@@ -1009,13 +1153,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1009 uccp.CircuitCode.Code, 1153 uccp.CircuitCode.Code,
1010 uccp.CircuitCode.ID, 1154 uccp.CircuitCode.ID,
1011 uccp.CircuitCode.SessionID, 1155 uccp.CircuitCode.SessionID,
1012 remoteEndPoint, 1156 endPoint,
1013 sessionInfo); 1157 sessionInfo);
1014 1158
1015 // Send ack straight away to let the viewer know that the connection is active. 1159 // Send ack straight away to let the viewer know that the connection is active.
1016 // The client will be null if it already exists (e.g. if on a region crossing the client sends a use 1160 // The client will be null if it already exists (e.g. if on a region crossing the client sends a use
1017 // circuit code to the existing child agent. This is not particularly obvious. 1161 // circuit code to the existing child agent. This is not particularly obvious.
1018 SendAckImmediate(remoteEndPoint, uccp.Header.Sequence); 1162 SendAckImmediate(endPoint, uccp.Header.Sequence);
1019 1163
1020 // We only want to send initial data to new clients, not ones which are being converted from child to root. 1164 // We only want to send initial data to new clients, not ones which are being converted from child to root.
1021 if (client != null) 1165 if (client != null)
@@ -1026,7 +1170,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1026 // Don't create clients for unauthorized requesters. 1170 // Don't create clients for unauthorized requesters.
1027 m_log.WarnFormat( 1171 m_log.WarnFormat(
1028 "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}", 1172 "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}",
1029 uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint); 1173 uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint);
1030 } 1174 }
1031 1175
1032 // m_log.DebugFormat( 1176 // m_log.DebugFormat(
@@ -1038,7 +1182,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1038 { 1182 {
1039 m_log.ErrorFormat( 1183 m_log.ErrorFormat(
1040 "[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}", 1184 "[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}",
1041 remoteEndPoint != null ? remoteEndPoint.ToString() : "n/a", 1185 endPoint != null ? endPoint.ToString() : "n/a",
1042 client != null ? client.Name : "unknown", 1186 client != null ? client.Name : "unknown",
1043 client != null ? client.AgentId.ToString() : "unknown", 1187 client != null ? client.AgentId.ToString() : "unknown",
1044 e.Message, 1188 e.Message,
@@ -1103,20 +1247,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1103 { 1247 {
1104 IClientAPI client = null; 1248 IClientAPI client = null;
1105 1249
1106 // In priciple there shouldn't be more than one thread here, ever. 1250 // We currently synchronize this code across the whole scene to avoid issues such as
1107 // But in case that happens, we need to synchronize this piece of code 1251 // http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done
1108 // because it's too important 1252 // consistently, this lock could probably be removed.
1109 lock (this) 1253 lock (this)
1110 { 1254 {
1111 if (!m_scene.TryGetClient(agentID, out client)) 1255 if (!m_scene.TryGetClient(agentID, out client))
1112 { 1256 {
1113 LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); 1257 LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
1114 1258
1115 client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); 1259 client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
1116 client.OnLogout += LogoutHandler; 1260 client.OnLogout += LogoutHandler;
1117 1261
1118 ((LLClientView)client).DisableFacelights = m_disableFacelights; 1262 ((LLClientView)client).DisableFacelights = m_disableFacelights;
1119 1263
1120 client.Start(); 1264 client.Start();
1121 } 1265 }
1122 } 1266 }
@@ -1155,7 +1299,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1155 // on to en-US to avoid number parsing issues 1299 // on to en-US to avoid number parsing issues
1156 Culture.SetCurrentCulture(); 1300 Culture.SetCurrentCulture();
1157 1301
1158 while (base.IsRunning) 1302 while (IsRunningInbound)
1159 { 1303 {
1160 try 1304 try
1161 { 1305 {
@@ -1170,7 +1314,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1170 } 1314 }
1171 1315
1172 if (packetInbox.Dequeue(100, ref incomingPacket)) 1316 if (packetInbox.Dequeue(100, ref incomingPacket))
1317 {
1173 ProcessInPacket(incomingPacket);//, incomingPacket); Util.FireAndForget(ProcessInPacket, incomingPacket); 1318 ProcessInPacket(incomingPacket);//, incomingPacket); Util.FireAndForget(ProcessInPacket, incomingPacket);
1319
1320 if (UsePools)
1321 m_incomingPacketPool.ReturnObject(incomingPacket);
1322 }
1174 } 1323 }
1175 catch (Exception ex) 1324 catch (Exception ex)
1176 { 1325 {
@@ -1197,7 +1346,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1197 // Action generic every round 1346 // Action generic every round
1198 Action<IClientAPI> clientPacketHandler = ClientOutgoingPacketHandler; 1347 Action<IClientAPI> clientPacketHandler = ClientOutgoingPacketHandler;
1199 1348
1200 while (base.IsRunning) 1349 while (base.IsRunningOutbound)
1201 { 1350 {
1202 try 1351 try
1203 { 1352 {
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
index 039379d..6e6b3ef 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
@@ -30,6 +30,7 @@ using System.Net;
30using System.Net.Sockets; 30using System.Net.Sockets;
31using System.Threading; 31using System.Threading;
32using log4net; 32using log4net;
33using OpenSim.Framework;
33 34
34namespace OpenMetaverse 35namespace OpenMetaverse
35{ 36{
@@ -58,17 +59,29 @@ namespace OpenMetaverse
58 /// <summary>Flag to process packets asynchronously or synchronously</summary> 59 /// <summary>Flag to process packets asynchronously or synchronously</summary>
59 private bool m_asyncPacketHandling; 60 private bool m_asyncPacketHandling;
60 61
61 /// <summary>The all important shutdown flag</summary> 62 /// <summary>
62 private volatile bool m_shutdownFlag = true; 63 /// Pool to use for handling data. May be null if UsePools = false;
64 /// </summary>
65 protected OpenSim.Framework.Pool<UDPPacketBuffer> m_pool;
63 66
64 /// <summary>Returns true if the server is currently listening, otherwise false</summary> 67 /// <summary>
65 public bool IsRunning { get { return !m_shutdownFlag; } } 68 /// Are we to use object pool(s) to reduce memory churn when receiving data?
69 /// </summary>
70 public bool UsePools { get; protected set; }
71
72 /// <summary>Returns true if the server is currently listening for inbound packets, otherwise false</summary>
73 public bool IsRunningInbound { get; private set; }
74
75 /// <summary>Returns true if the server is currently sending outbound packets, otherwise false</summary>
76 /// <remarks>If IsRunningOut = false, then any request to send a packet is simply dropped.</remarks>
77 public bool IsRunningOutbound { get; private set; }
66 78
67 /// <summary> 79 /// <summary>
68 /// Default constructor 80 /// Default constructor
69 /// </summary> 81 /// </summary>
70 /// <param name="bindAddress">Local IP address to bind the server to</param> 82 /// <param name="bindAddress">Local IP address to bind the server to</param>
71 /// <param name="port">Port to listening for incoming UDP packets on</param> 83 /// <param name="port">Port to listening for incoming UDP packets on</param>
84 /// /// <param name="usePool">Are we to use an object pool to get objects for handing inbound data?</param>
72 public OpenSimUDPBase(IPAddress bindAddress, int port) 85 public OpenSimUDPBase(IPAddress bindAddress, int port)
73 { 86 {
74 m_localBindAddress = bindAddress; 87 m_localBindAddress = bindAddress;
@@ -76,7 +89,7 @@ namespace OpenMetaverse
76 } 89 }
77 90
78 /// <summary> 91 /// <summary>
79 /// Start the UDP server 92 /// Start inbound UDP packet handling.
80 /// </summary> 93 /// </summary>
81 /// <param name="recvBufferSize">The size of the receive buffer for 94 /// <param name="recvBufferSize">The size of the receive buffer for
82 /// the UDP socket. This value is passed up to the operating system 95 /// the UDP socket. This value is passed up to the operating system
@@ -91,11 +104,16 @@ namespace OpenMetaverse
91 /// manner (not throwing an exception when the remote side resets the 104 /// manner (not throwing an exception when the remote side resets the
92 /// connection). This call is ignored on Mono where the flag is not 105 /// connection). This call is ignored on Mono where the flag is not
93 /// necessary</remarks> 106 /// necessary</remarks>
94 public void Start(int recvBufferSize, bool asyncPacketHandling) 107 public void StartInbound(int recvBufferSize, bool asyncPacketHandling)
95 { 108 {
109 if (UsePools)
110 m_pool = new Pool<UDPPacketBuffer>(() => new UDPPacketBuffer(), 500);
111 else
112 m_pool = null;
113
96 m_asyncPacketHandling = asyncPacketHandling; 114 m_asyncPacketHandling = asyncPacketHandling;
97 115
98 if (m_shutdownFlag) 116 if (!IsRunningInbound)
99 { 117 {
100 const int SIO_UDP_CONNRESET = -1744830452; 118 const int SIO_UDP_CONNRESET = -1744830452;
101 119
@@ -127,8 +145,7 @@ namespace OpenMetaverse
127 145
128 m_udpSocket.Bind(ipep); 146 m_udpSocket.Bind(ipep);
129 147
130 // we're not shutting down, we're starting up 148 IsRunningInbound = true;
131 m_shutdownFlag = false;
132 149
133 // kick off an async receive. The Start() method will return, the 150 // kick off an async receive. The Start() method will return, the
134 // actual receives will occur asynchronously and will be caught in 151 // actual receives will occur asynchronously and will be caught in
@@ -138,28 +155,41 @@ namespace OpenMetaverse
138 } 155 }
139 156
140 /// <summary> 157 /// <summary>
141 /// Stops the UDP server 158 /// Start outbound UDP packet handling.
142 /// </summary> 159 /// </summary>
143 public void Stop() 160 public void StartOutbound()
144 { 161 {
145 if (!m_shutdownFlag) 162 IsRunningOutbound = true;
163 }
164
165 public void StopInbound()
166 {
167 if (IsRunningInbound)
146 { 168 {
147 // wait indefinitely for a writer lock. Once this is called, the .NET runtime 169 // wait indefinitely for a writer lock. Once this is called, the .NET runtime
148 // will deny any more reader locks, in effect blocking all other send/receive 170 // will deny any more reader locks, in effect blocking all other send/receive
149 // threads. Once we have the lock, we set shutdownFlag to inform the other 171 // threads. Once we have the lock, we set IsRunningInbound = false to inform the other
150 // threads that the socket is closed. 172 // threads that the socket is closed.
151 m_shutdownFlag = true; 173 IsRunningInbound = false;
152 m_udpSocket.Close(); 174 m_udpSocket.Close();
153 } 175 }
154 } 176 }
155 177
178 public void StopOutbound()
179 {
180 IsRunningOutbound = false;
181 }
182
156 private void AsyncBeginReceive() 183 private void AsyncBeginReceive()
157 { 184 {
158 // allocate a packet buffer 185 UDPPacketBuffer buf;
159 //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut(); 186
160 UDPPacketBuffer buf = new UDPPacketBuffer(); 187 if (UsePools)
188 buf = m_pool.GetObject();
189 else
190 buf = new UDPPacketBuffer();
161 191
162 if (!m_shutdownFlag) 192 if (IsRunningInbound)
163 { 193 {
164 try 194 try
165 { 195 {
@@ -212,7 +242,7 @@ namespace OpenMetaverse
212 { 242 {
213 // Asynchronous receive operations will complete here through the call 243 // Asynchronous receive operations will complete here through the call
214 // to AsyncBeginReceive 244 // to AsyncBeginReceive
215 if (!m_shutdownFlag) 245 if (IsRunningInbound)
216 { 246 {
217 // Asynchronous mode will start another receive before the 247 // Asynchronous mode will start another receive before the
218 // callback for this packet is even fired. Very parallel :-) 248 // callback for this packet is even fired. Very parallel :-)
@@ -221,8 +251,6 @@ namespace OpenMetaverse
221 251
222 // get the buffer that was created in AsyncBeginReceive 252 // get the buffer that was created in AsyncBeginReceive
223 // this is the received data 253 // this is the received data
224 //WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState;
225 //UDPPacketBuffer buffer = wrappedBuffer.Instance;
226 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; 254 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
227 255
228 try 256 try
@@ -239,7 +267,8 @@ namespace OpenMetaverse
239 catch (ObjectDisposedException) { } 267 catch (ObjectDisposedException) { }
240 finally 268 finally
241 { 269 {
242 //wrappedBuffer.Dispose(); 270 if (UsePools)
271 m_pool.ReturnObject(buffer);
243 272
244 // Synchronous mode waits until the packet callback completes 273 // Synchronous mode waits until the packet callback completes
245 // before starting the receive to fetch another packet 274 // before starting the receive to fetch another packet
@@ -252,7 +281,7 @@ namespace OpenMetaverse
252 281
253 public void AsyncBeginSend(UDPPacketBuffer buf) 282 public void AsyncBeginSend(UDPPacketBuffer buf)
254 { 283 {
255 if (!m_shutdownFlag) 284 if (IsRunningOutbound)
256 { 285 {
257 try 286 try
258 { 287 {
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs
index fc9406b..2a3d14f 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs
@@ -31,6 +31,7 @@ using System.Reflection;
31using OpenMetaverse; 31using OpenMetaverse;
32using OpenMetaverse.Packets; 32using OpenMetaverse.Packets;
33using log4net; 33using log4net;
34using OpenSim.Framework.Monitoring;
34 35
35namespace OpenSim.Region.ClientStack.LindenUDP 36namespace OpenSim.Region.ClientStack.LindenUDP
36{ 37{
@@ -43,17 +44,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
43 private bool packetPoolEnabled = true; 44 private bool packetPoolEnabled = true;
44 private bool dataBlockPoolEnabled = true; 45 private bool dataBlockPoolEnabled = true;
45 46
47 private PercentageStat m_packetsReusedStat = new PercentageStat(
48 "PacketsReused",
49 "Packets reused",
50 "clientstack",
51 "packetpool",
52 StatVerbosity.Debug,
53 "Number of packets reused out of all requests to the packet pool");
54
55 private PercentageStat m_blocksReusedStat = new PercentageStat(
56 "BlocksReused",
57 "Blocks reused",
58 "clientstack",
59 "packetpool",
60 StatVerbosity.Debug,
61 "Number of data blocks reused out of all requests to the packet pool");
62
46 /// <summary> 63 /// <summary>
47 /// Pool of packets available for reuse. 64 /// Pool of packets available for reuse.
48 /// </summary> 65 /// </summary>
49 private readonly Dictionary<PacketType, Stack<Packet>> pool = new Dictionary<PacketType, Stack<Packet>>(); 66 private readonly Dictionary<PacketType, Stack<Packet>> pool = new Dictionary<PacketType, Stack<Packet>>();
50 67
51 private static Dictionary<Type, Stack<Object>> DataBlocks = 68 private static Dictionary<Type, Stack<Object>> DataBlocks = new Dictionary<Type, Stack<Object>>();
52 new Dictionary<Type, Stack<Object>>();
53
54 static PacketPool()
55 {
56 }
57 69
58 public static PacketPool Instance 70 public static PacketPool Instance
59 { 71 {
@@ -72,8 +84,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
72 get { return dataBlockPoolEnabled; } 84 get { return dataBlockPoolEnabled; }
73 } 85 }
74 86
87 private PacketPool()
88 {
89 StatsManager.RegisterStat(m_packetsReusedStat);
90 StatsManager.RegisterStat(m_blocksReusedStat);
91 }
92
93 /// <summary>
94 /// Gets a packet of the given type.
95 /// </summary>
96 /// <param name='type'></param>
97 /// <returns>Guaranteed to always return a packet, whether from the pool or newly constructed.</returns>
75 public Packet GetPacket(PacketType type) 98 public Packet GetPacket(PacketType type)
76 { 99 {
100 m_packetsReusedStat.Consequent++;
101
77 Packet packet; 102 Packet packet;
78 103
79 if (!packetPoolEnabled) 104 if (!packetPoolEnabled)
@@ -83,13 +108,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
83 { 108 {
84 if (!pool.ContainsKey(type) || pool[type] == null || (pool[type]).Count == 0) 109 if (!pool.ContainsKey(type) || pool[type] == null || (pool[type]).Count == 0)
85 { 110 {
111// m_log.DebugFormat("[PACKETPOOL]: Building {0} packet", type);
112
86 // Creating a new packet if we cannot reuse an old package 113 // Creating a new packet if we cannot reuse an old package
87 packet = Packet.BuildPacket(type); 114 packet = Packet.BuildPacket(type);
88 } 115 }
89 else 116 else
90 { 117 {
118// m_log.DebugFormat("[PACKETPOOL]: Pulling {0} packet", type);
119
91 // Recycle old packages 120 // Recycle old packages
92 packet = (pool[type]).Pop(); 121 m_packetsReusedStat.Antecedent++;
122
123 packet = pool[type].Pop();
93 } 124 }
94 } 125 }
95 126
@@ -138,7 +169,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
138 { 169 {
139 PacketType type = GetType(bytes); 170 PacketType type = GetType(bytes);
140 171
141 Array.Clear(zeroBuffer, 0, zeroBuffer.Length); 172// Array.Clear(zeroBuffer, 0, zeroBuffer.Length);
142 173
143 int i = 0; 174 int i = 0;
144 Packet packet = GetPacket(type); 175 Packet packet = GetPacket(type);
@@ -185,6 +216,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
185 switch (packet.Type) 216 switch (packet.Type)
186 { 217 {
187 // List pooling packets here 218 // List pooling packets here
219 case PacketType.AgentUpdate:
188 case PacketType.PacketAck: 220 case PacketType.PacketAck:
189 case PacketType.ObjectUpdate: 221 case PacketType.ObjectUpdate:
190 case PacketType.ImprovedTerseObjectUpdate: 222 case PacketType.ImprovedTerseObjectUpdate:
@@ -199,7 +231,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
199 231
200 if ((pool[type]).Count < 50) 232 if ((pool[type]).Count < 50)
201 { 233 {
202 (pool[type]).Push(packet); 234// m_log.DebugFormat("[PACKETPOOL]: Pushing {0} packet", type);
235
236 pool[type].Push(packet);
203 } 237 }
204 } 238 }
205 break; 239 break;
@@ -211,16 +245,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
211 } 245 }
212 } 246 }
213 247
214 public static T GetDataBlock<T>() where T: new() 248 public T GetDataBlock<T>() where T: new()
215 { 249 {
216 lock (DataBlocks) 250 lock (DataBlocks)
217 { 251 {
252 m_blocksReusedStat.Consequent++;
253
218 Stack<Object> s; 254 Stack<Object> s;
219 255
220 if (DataBlocks.TryGetValue(typeof(T), out s)) 256 if (DataBlocks.TryGetValue(typeof(T), out s))
221 { 257 {
222 if (s.Count > 0) 258 if (s.Count > 0)
259 {
260 m_blocksReusedStat.Antecedent++;
223 return (T)s.Pop(); 261 return (T)s.Pop();
262 }
224 } 263 }
225 else 264 else
226 { 265 {
@@ -231,7 +270,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
231 } 270 }
232 } 271 }
233 272
234 public static void ReturnDataBlock<T>(T block) where T: new() 273 public void ReturnDataBlock<T>(T block) where T: new()
235 { 274 {
236 if (block == null) 275 if (block == null)
237 return; 276 return;
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs
index 109a8e1..556df30 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs
@@ -43,7 +43,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
43 /// This will contain basic tests for the LindenUDP client stack 43 /// This will contain basic tests for the LindenUDP client stack
44 /// </summary> 44 /// </summary>
45 [TestFixture] 45 [TestFixture]
46 public class BasicCircuitTests 46 public class BasicCircuitTests : OpenSimTestCase
47 { 47 {
48 private Scene m_scene; 48 private Scene m_scene;
49 private TestLLUDPServer m_udpServer; 49 private TestLLUDPServer m_udpServer;
@@ -65,8 +65,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
65 } 65 }
66 66
67 [SetUp] 67 [SetUp]
68 public void SetUp() 68 public override void SetUp()
69 { 69 {
70 base.SetUp();
70 m_scene = new SceneHelpers().SetupScene(); 71 m_scene = new SceneHelpers().SetupScene();
71 } 72 }
72 73
@@ -143,7 +144,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
143 public void TestAddClient() 144 public void TestAddClient()
144 { 145 {
145 TestHelpers.InMethod(); 146 TestHelpers.InMethod();
146// XmlConfigurator.Configure(); 147// TestHelpers.EnableLogging();
147 148
148 AddUdpServer(); 149 AddUdpServer();
149 150
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
index 11db18a..f1903c3 100644
--- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
@@ -28,6 +28,7 @@
28using System; 28using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Generic; 30using System.Collections.Generic;
31using System.Linq;
31using System.Reflection; 32using System.Reflection;
32using System.Threading; 33using System.Threading;
33using log4net; 34using log4net;
@@ -495,42 +496,36 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
495 496
496 protected virtual void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online) 497 protected virtual void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online)
497 { 498 {
498 foreach (FriendInfo friend in friendList) 499 List<string> friendStringIds = friendList.ConvertAll<string>(friend => friend.Friend);
500 List<string> remoteFriendStringIds = new List<string>();
501 foreach (string friendStringId in friendStringIds)
499 { 502 {
500 UUID friendID; 503 UUID friendUuid;
501 if (UUID.TryParse(friend.Friend, out friendID)) 504 if (UUID.TryParse(friendStringId, out friendUuid))
502 { 505 {
503 // Try local 506 if (LocalStatusNotification(userID, friendUuid, online))
504 if (LocalStatusNotification(userID, friendID, online))
505 continue; 507 continue;
506 508
507 // The friend is not here [as root]. Let's forward. 509 remoteFriendStringIds.Add(friendStringId);
508 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
509 if (friendSessions != null && friendSessions.Length > 0)
510 {
511 PresenceInfo friendSession = null;
512 foreach (PresenceInfo pinfo in friendSessions)
513 {
514 if (pinfo.RegionID != UUID.Zero) // let's guard against sessions-gone-bad
515 {
516 friendSession = pinfo;
517 break;
518 }
519 }
520
521 if (friendSession != null)
522 {
523 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
524 //m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName);
525 m_FriendsSimConnector.StatusNotify(region, userID, friendID, online);
526 }
527 }
528
529 // Friend is not online. Ignore.
530 } 510 }
531 else 511 else
532 { 512 {
533 m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friend.Friend); 513 m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friendStringId);
514 }
515 }
516
517 // We do this regrouping so that we can efficiently send a single request rather than one for each
518 // friend in what may be a very large friends list.
519 PresenceInfo[] friendSessions = PresenceService.GetAgents(remoteFriendStringIds.ToArray());
520
521 foreach (PresenceInfo friendSession in friendSessions)
522 {
523 // let's guard against sessions-gone-bad
524 if (friendSession.RegionID != UUID.Zero)
525 {
526 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
527 //m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName);
528 m_FriendsSimConnector.StatusNotify(region, userID, friendSession.UserID, online);
534 } 529 }
535 } 530 }
536 } 531 }
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
index 617a350..90fe430 100644
--- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
@@ -486,6 +486,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
486 if (sp.ParentID != (uint)0) 486 if (sp.ParentID != (uint)0)
487 sp.StandUp(); 487 sp.StandUp();
488 488
489 // At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to
490 // the viewer. However, it might mean that the viewer does not see the black teleport screen (untested).
489 sp.ControllingClient.SendTeleportStart(teleportFlags); 491 sp.ControllingClient.SendTeleportStart(teleportFlags);
490 492
491 // the avatar.Close below will clear the child region list. We need this below for (possibly) 493 // 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
561 // So let's wait 563 // So let's wait
562 Thread.Sleep(200); 564 Thread.Sleep(200);
563 565
566 // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears
567 // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly
568 // only on TeleportFinish). This is untested for region teleport between different simulators
569 // though this probably also works.
564 m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath); 570 m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath);
565
566 } 571 }
567 else 572 else
568 { 573 {
diff --git a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
index 1f340df..93a045e 100644
--- a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
@@ -42,7 +42,7 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
42{ 42{
43 public class DynamicTextureModule : IRegionModule, IDynamicTextureManager 43 public class DynamicTextureModule : IRegionModule, IDynamicTextureManager
44 { 44 {
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 45// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46 46
47 private const int ALL_SIDES = -1; 47 private const int ALL_SIDES = -1;
48 48
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
index a6923ef..ea806ec 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
@@ -290,7 +290,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
290 290
291 foreach (DearchiveContext sceneContext in sceneContexts.Values) 291 foreach (DearchiveContext sceneContext in sceneContexts.Values)
292 { 292 {
293 m_log.InfoFormat("[ARCHIVER:] Loading region {0}", sceneContext.Scene.RegionInfo.RegionName); 293 m_log.InfoFormat("[ARCHIVER]: Loading region {0}", sceneContext.Scene.RegionInfo.RegionName);
294 294
295 if (!m_merge) 295 if (!m_merge)
296 { 296 {
@@ -324,7 +324,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
324 Util.FireAndForget(delegate(object o) 324 Util.FireAndForget(delegate(object o)
325 { 325 {
326 Thread.Sleep(15000); 326 Thread.Sleep(15000);
327 m_log.Info("Starting scripts in scene objects"); 327 m_log.Info("[ARCHIVER]: Starting scripts in scene objects");
328 328
329 foreach (DearchiveContext sceneContext in sceneContexts.Values) 329 foreach (DearchiveContext sceneContext in sceneContexts.Values)
330 { 330 {
diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
index 8b7406d..95edf62 100644
--- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
+++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
@@ -207,6 +207,7 @@ namespace OpenSim.Region.CoreModules.World.Land
207 client.OnParcelInfoRequest += ClientOnParcelInfoRequest; 207 client.OnParcelInfoRequest += ClientOnParcelInfoRequest;
208 client.OnParcelDeedToGroup += ClientOnParcelDeedToGroup; 208 client.OnParcelDeedToGroup += ClientOnParcelDeedToGroup;
209 client.OnPreAgentUpdate += ClientOnPreAgentUpdate; 209 client.OnPreAgentUpdate += ClientOnPreAgentUpdate;
210 client.OnParcelDwellRequest += ClientOnParcelDwellRequest;
210 211
211 EntityBase presenceEntity; 212 EntityBase presenceEntity;
212 if (m_scene.Entities.TryGetValue(client.AgentId, out presenceEntity) && presenceEntity is ScenePresence) 213 if (m_scene.Entities.TryGetValue(client.AgentId, out presenceEntity) && presenceEntity is ScenePresence)
@@ -798,6 +799,17 @@ namespace OpenSim.Region.CoreModules.World.Land
798 } 799 }
799 } 800 }
800 801
802 private void ClientOnParcelDwellRequest(int localID, IClientAPI client)
803 {
804 ILandObject parcel = null;
805 lock (m_landList)
806 {
807 if (!m_landList.TryGetValue(localID, out parcel))
808 return;
809 }
810 client.SendParcelDwellReply(localID, parcel.LandData.GlobalID, parcel.LandData.Dwell);
811 }
812
801 #endregion 813 #endregion
802 814
803 #region Parcel Modification 815 #region Parcel Modification
diff --git a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
index 6e39e9a..87241e1 100644
--- a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
+++ b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
@@ -27,6 +27,7 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Linq;
30using System.Reflection; 31using System.Reflection;
31using System.Text; 32using System.Text;
32using System.Text.RegularExpressions; 33using System.Text.RegularExpressions;
@@ -83,53 +84,87 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
83 m_console.Commands.AddCommand( 84 m_console.Commands.AddCommand(
84 "Objects", false, "delete object owner", 85 "Objects", false, "delete object owner",
85 "delete object owner <UUID>", 86 "delete object owner <UUID>",
86 "Delete a scene object by owner", HandleDeleteObject); 87 "Delete scene objects by owner",
88 "Command will ask for confirmation before proceeding.",
89 HandleDeleteObject);
87 90
88 m_console.Commands.AddCommand( 91 m_console.Commands.AddCommand(
89 "Objects", false, "delete object creator", 92 "Objects", false, "delete object creator",
90 "delete object creator <UUID>", 93 "delete object creator <UUID>",
91 "Delete a scene object by creator", HandleDeleteObject); 94 "Delete scene objects by creator",
95 "Command will ask for confirmation before proceeding.",
96 HandleDeleteObject);
92 97
93 m_console.Commands.AddCommand( 98 m_console.Commands.AddCommand(
94 "Objects", false, "delete object uuid", 99 "Objects", false, "delete object uuid",
95 "delete object uuid <UUID>", 100 "delete object uuid <UUID>",
96 "Delete a scene object by uuid", HandleDeleteObject); 101 "Delete a scene object by uuid",
102 HandleDeleteObject);
97 103
98 m_console.Commands.AddCommand( 104 m_console.Commands.AddCommand(
99 "Objects", false, "delete object name", 105 "Objects", false, "delete object name",
100 "delete object name [--regex] <name>", 106 "delete object name [--regex] <name>",
101 "Delete a scene object by name.", 107 "Delete a scene object by name.",
102 "If --regex is specified then the name is treatead as a regular expression", 108 "Command will ask for confirmation before proceeding.\n"
109 + "If --regex is specified then the name is treatead as a regular expression",
103 HandleDeleteObject); 110 HandleDeleteObject);
104 111
105 m_console.Commands.AddCommand( 112 m_console.Commands.AddCommand(
106 "Objects", false, "delete object outside", 113 "Objects", false, "delete object outside",
107 "delete object outside", 114 "delete object outside",
108 "Delete all scene objects outside region boundaries", HandleDeleteObject); 115 "Delete all scene objects outside region boundaries",
116 "Command will ask for confirmation before proceeding.",
117 HandleDeleteObject);
118
119 m_console.Commands.AddCommand(
120 "Objects",
121 false,
122 "delete object pos",
123 "delete object pos <start-coord> to <end-coord>",
124 "Delete scene objects within the given area.",
125 "Each component of the coord is comma separated. There must be no spaces between the commas.\n"
126 + "If you don't care about the z component you can simply omit it.\n"
127 + "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n"
128 + "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n"
129 + "If you want to specify the minimum value of a component then you can use -~ instead of a number\n"
130 + "e.g.\n"
131 + "delete object pos 20,20,20 to 40,40,40\n"
132 + "delete object pos 20,20 to 40,40\n"
133 + "delete object pos ,20,20 to ,40,40\n"
134 + "delete object pos ,,30 to ,,~\n"
135 + "delete object pos ,,-~ to ,,30",
136 HandleDeleteObject);
109 137
110 m_console.Commands.AddCommand( 138 m_console.Commands.AddCommand(
111 "Objects", 139 "Objects",
112 false, 140 false,
113 "show object uuid", 141 "show object uuid",
114 "show object uuid <UUID>", 142 "show object uuid [--full] <UUID>",
115 "Show details of a scene object with the given UUID", HandleShowObjectByUuid); 143 "Show details of a scene object with the given UUID",
144 "The --full option will print out information on all the parts of the object.\n"
145 + "For yet more detailed part information, use the \"show part\" commands.",
146 HandleShowObjectByUuid);
116 147
117 m_console.Commands.AddCommand( 148 m_console.Commands.AddCommand(
118 "Objects", 149 "Objects",
119 false, 150 false,
120 "show object name", 151 "show object name",
121 "show object name [--regex] <name>", 152 "show object name [--full] [--regex] <name>",
122 "Show details of scene objects with the given name.", 153 "Show details of scene objects with the given name.",
123 "If --regex is specified then the name is treatead as a regular expression", 154 "The --full option will print out information on all the parts of the object.\n"
155 + "For yet more detailed part information, use the \"show part\" commands.\n"
156 + "If --regex is specified then the name is treatead as a regular expression.",
124 HandleShowObjectByName); 157 HandleShowObjectByName);
125 158
126 m_console.Commands.AddCommand( 159 m_console.Commands.AddCommand(
127 "Objects", 160 "Objects",
128 false, 161 false,
129 "show object pos", 162 "show object pos",
130 "show object pos <start-coord> to <end-coord>", 163 "show object pos [--full] <start-coord> to <end-coord>",
131 "Show details of scene objects within the given area.", 164 "Show details of scene objects within the given area.",
132 "Each component of the coord is comma separated. There must be no spaces between the commas.\n" 165 "The --full option will print out information on all the parts of the object.\n"
166 + "For yet more detailed part information, use the \"show part\" commands.\n"
167 + "Each component of the coord is comma separated. There must be no spaces between the commas.\n"
133 + "If you don't care about the z component you can simply omit it.\n" 168 + "If you don't care about the z component you can simply omit it.\n"
134 + "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n" 169 + "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n"
135 + "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n" 170 + "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
188// m_log.DebugFormat("[OBJECTS COMMANDS MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName); 223// m_log.DebugFormat("[OBJECTS COMMANDS MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName);
189 } 224 }
190 225
191 private void OutputSogsToConsole(Predicate<SceneObjectGroup> searchPredicate) 226 /// <summary>
227 /// Outputs the sogs to console.
228 /// </summary>
229 /// <param name='searchPredicate'></param>
230 /// <param name='showFull'>If true then output all part details. If false then output summary.</param>
231 private void OutputSogsToConsole(Predicate<SceneObjectGroup> searchPredicate, bool showFull)
192 { 232 {
193 List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups().FindAll(searchPredicate); 233 List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups().FindAll(searchPredicate);
194 234
@@ -196,7 +236,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
196 236
197 foreach (SceneObjectGroup so in sceneObjects) 237 foreach (SceneObjectGroup so in sceneObjects)
198 { 238 {
199 AddSceneObjectReport(sb, so); 239 AddSceneObjectReport(sb, so, showFull);
200 sb.Append("\n"); 240 sb.Append("\n");
201 } 241 }
202 242
@@ -205,7 +245,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
205 m_console.OutputFormat(sb.ToString()); 245 m_console.OutputFormat(sb.ToString());
206 } 246 }
207 247
208 private void OutputSopsToConsole(Predicate<SceneObjectPart> searchPredicate) 248 private void OutputSopsToConsole(Predicate<SceneObjectPart> searchPredicate, bool showFull)
209 { 249 {
210 List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups(); 250 List<SceneObjectGroup> sceneObjects = m_scene.GetSceneObjectGroups();
211 List<SceneObjectPart> parts = new List<SceneObjectPart>(); 251 List<SceneObjectPart> parts = new List<SceneObjectPart>();
@@ -216,7 +256,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
216 256
217 foreach (SceneObjectPart part in parts) 257 foreach (SceneObjectPart part in parts)
218 { 258 {
219 AddScenePartReport(sb, part); 259 AddScenePartReport(sb, part, showFull);
220 sb.Append("\n"); 260 sb.Append("\n");
221 } 261 }
222 262
@@ -225,21 +265,26 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
225 m_console.OutputFormat(sb.ToString()); 265 m_console.OutputFormat(sb.ToString());
226 } 266 }
227 267
228 private void HandleShowObjectByUuid(string module, string[] cmd) 268 private void HandleShowObjectByUuid(string module, string[] cmdparams)
229 { 269 {
230 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) 270 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
231 return; 271 return;
232 272
233 if (cmd.Length < 4) 273 bool showFull = false;
274 OptionSet options = new OptionSet().Add("full", v => showFull = v != null );
275
276 List<string> mainParams = options.Parse(cmdparams);
277
278 if (mainParams.Count < 4)
234 { 279 {
235 m_console.OutputFormat("Usage: show object uuid <uuid>"); 280 m_console.OutputFormat("Usage: show object uuid <uuid>");
236 return; 281 return;
237 } 282 }
238 283
239 UUID objectUuid; 284 UUID objectUuid;
240 if (!UUID.TryParse(cmd[3], out objectUuid)) 285 if (!UUID.TryParse(mainParams[3], out objectUuid))
241 { 286 {
242 m_console.OutputFormat("{0} is not a valid uuid", cmd[3]); 287 m_console.OutputFormat("{0} is not a valid uuid", mainParams[3]);
243 return; 288 return;
244 } 289 }
245 290
@@ -252,7 +297,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
252 } 297 }
253 298
254 StringBuilder sb = new StringBuilder(); 299 StringBuilder sb = new StringBuilder();
255 AddSceneObjectReport(sb, so); 300 AddSceneObjectReport(sb, so, showFull);
256 301
257 m_console.OutputFormat(sb.ToString()); 302 m_console.OutputFormat(sb.ToString());
258 } 303 }
@@ -262,14 +307,17 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
262 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) 307 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
263 return; 308 return;
264 309
310 bool showFull = false;
265 bool useRegex = false; 311 bool useRegex = false;
266 OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null ); 312 OptionSet options = new OptionSet();
313 options.Add("full", v => showFull = v != null );
314 options.Add("regex", v => useRegex = v != null );
267 315
268 List<string> mainParams = options.Parse(cmdparams); 316 List<string> mainParams = options.Parse(cmdparams);
269 317
270 if (mainParams.Count < 4) 318 if (mainParams.Count < 4)
271 { 319 {
272 m_console.OutputFormat("Usage: show object name [--regex] <name>"); 320 m_console.OutputFormat("Usage: show object name [--full] [--regex] <name>");
273 return; 321 return;
274 } 322 }
275 323
@@ -287,7 +335,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
287 searchPredicate = so => so.Name == name; 335 searchPredicate = so => so.Name == name;
288 } 336 }
289 337
290 OutputSogsToConsole(searchPredicate); 338 OutputSogsToConsole(searchPredicate, showFull);
291 } 339 }
292 340
293 private void HandleShowObjectByPos(string module, string[] cmdparams) 341 private void HandleShowObjectByPos(string module, string[] cmdparams)
@@ -295,51 +343,49 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
295 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) 343 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
296 return; 344 return;
297 345
298 if (cmdparams.Length < 5) 346 bool showFull = false;
299 { 347 OptionSet options = new OptionSet().Add("full", v => showFull = v != null );
300 m_console.OutputFormat("Usage: show object pos <start-coord> to <end-coord>");
301 return;
302 }
303 348
304 string rawConsoleStartVector = cmdparams[3]; 349 List<string> mainParams = options.Parse(cmdparams);
305 Vector3 startVector;
306 350
307 if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector)) 351 if (mainParams.Count < 5)
308 { 352 {
309 m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector); 353 m_console.OutputFormat("Usage: show object pos [--full] <start-coord> to <end-coord>");
310 return; 354 return;
311 } 355 }
312 356
313 string rawConsoleEndVector = cmdparams[5]; 357 Vector3 startVector, endVector;
314 Vector3 endVector;
315 358
316 if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector)) 359 if (!TryParseVectorRange(cmdparams.Skip(3).Take(3), out startVector, out endVector))
317 {
318 m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector);
319 return; 360 return;
320 }
321 361
322 Predicate<SceneObjectGroup> searchPredicate 362 Predicate<SceneObjectGroup> searchPredicate
323 = so => Util.IsInsideBox(so.AbsolutePosition, startVector, endVector); 363 = so => Util.IsInsideBox(so.AbsolutePosition, startVector, endVector);
324 364
325 OutputSogsToConsole(searchPredicate); 365 OutputSogsToConsole(searchPredicate, showFull);
326 } 366 }
327 367
328 private void HandleShowPartByUuid(string module, string[] cmd) 368 private void HandleShowPartByUuid(string module, string[] cmdparams)
329 { 369 {
330 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) 370 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
331 return; 371 return;
332 372
333 if (cmd.Length < 4) 373// bool showFull = false;
374 OptionSet options = new OptionSet();
375// options.Add("full", v => showFull = v != null );
376
377 List<string> mainParams = options.Parse(cmdparams);
378
379 if (mainParams.Count < 4)
334 { 380 {
335 m_console.OutputFormat("Usage: show part uuid <uuid>"); 381 m_console.OutputFormat("Usage: show part uuid [--full] <uuid>");
336 return; 382 return;
337 } 383 }
338 384
339 UUID objectUuid; 385 UUID objectUuid;
340 if (!UUID.TryParse(cmd[3], out objectUuid)) 386 if (!UUID.TryParse(mainParams[3], out objectUuid))
341 { 387 {
342 m_console.OutputFormat("{0} is not a valid uuid", cmd[3]); 388 m_console.OutputFormat("{0} is not a valid uuid", mainParams[3]);
343 return; 389 return;
344 } 390 }
345 391
@@ -352,7 +398,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
352 } 398 }
353 399
354 StringBuilder sb = new StringBuilder(); 400 StringBuilder sb = new StringBuilder();
355 AddScenePartReport(sb, sop); 401 AddScenePartReport(sb, sop, true);
356 402
357 m_console.OutputFormat(sb.ToString()); 403 m_console.OutputFormat(sb.ToString());
358 } 404 }
@@ -362,13 +408,19 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
362 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) 408 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
363 return; 409 return;
364 410
365 if (cmdparams.Length < 5) 411// bool showFull = false;
412 OptionSet options = new OptionSet();
413// options.Add("full", v => showFull = v != null );
414
415 List<string> mainParams = options.Parse(cmdparams);
416
417 if (mainParams.Count < 5)
366 { 418 {
367 m_console.OutputFormat("Usage: show part pos <start-coord> to <end-coord>"); 419 m_console.OutputFormat("Usage: show part pos [--full] <start-coord> to <end-coord>");
368 return; 420 return;
369 } 421 }
370 422
371 string rawConsoleStartVector = cmdparams[3]; 423 string rawConsoleStartVector = mainParams[3];
372 Vector3 startVector; 424 Vector3 startVector;
373 425
374 if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector)) 426 if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector))
@@ -377,7 +429,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
377 return; 429 return;
378 } 430 }
379 431
380 string rawConsoleEndVector = cmdparams[5]; 432 string rawConsoleEndVector = mainParams[5];
381 Vector3 endVector; 433 Vector3 endVector;
382 434
383 if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector)) 435 if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector))
@@ -386,7 +438,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
386 return; 438 return;
387 } 439 }
388 440
389 OutputSopsToConsole(sop => Util.IsInsideBox(sop.AbsolutePosition, startVector, endVector)); 441 OutputSopsToConsole(sop => Util.IsInsideBox(sop.AbsolutePosition, startVector, endVector), true);
390 } 442 }
391 443
392 private void HandleShowPartByName(string module, string[] cmdparams) 444 private void HandleShowPartByName(string module, string[] cmdparams)
@@ -394,14 +446,17 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
394 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) 446 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
395 return; 447 return;
396 448
449// bool showFull = false;
397 bool useRegex = false; 450 bool useRegex = false;
398 OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null ); 451 OptionSet options = new OptionSet();
452// options.Add("full", v => showFull = v != null );
453 options.Add("regex", v => useRegex = v != null );
399 454
400 List<string> mainParams = options.Parse(cmdparams); 455 List<string> mainParams = options.Parse(cmdparams);
401 456
402 if (mainParams.Count < 4) 457 if (mainParams.Count < 4)
403 { 458 {
404 m_console.OutputFormat("Usage: show part name [--regex] <name>"); 459 m_console.OutputFormat("Usage: show part name [--full] [--regex] <name>");
405 return; 460 return;
406 } 461 }
407 462
@@ -419,31 +474,112 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
419 searchPredicate = sop => sop.Name == name; 474 searchPredicate = sop => sop.Name == name;
420 } 475 }
421 476
422 OutputSopsToConsole(searchPredicate); 477 OutputSopsToConsole(searchPredicate, true);
423 } 478 }
424 479
425 private StringBuilder AddSceneObjectReport(StringBuilder sb, SceneObjectGroup so) 480 /// <summary>
481 /// Append a scene object report to an input StringBuilder
482 /// </summary>
483 /// <returns></returns>
484 /// <param name='sb'></param>
485 /// <param name='so'</param>
486 /// <param name='showFull'>
487 /// If true then information on all parts of an object is appended.
488 /// If false then only summary information about an object is appended.
489 /// </param>
490 private StringBuilder AddSceneObjectReport(StringBuilder sb, SceneObjectGroup so, bool showFull)
426 { 491 {
427 sb.AppendFormat("Name: {0}\n", so.Name); 492 if (showFull)
428 sb.AppendFormat("Description: {0}\n", so.Description); 493 {
429 sb.AppendFormat("Location: {0} @ {1}\n", so.AbsolutePosition, so.Scene.RegionInfo.RegionName); 494 foreach (SceneObjectPart sop in so.Parts)
430 sb.AppendFormat("Parts: {0}\n", so.PrimCount); 495 {
431 sb.AppendFormat("Flags: {0}\n", so.RootPart.Flags); 496 AddScenePartReport(sb, sop, false);
497 sb.Append("\n");
498 }
499 }
500 else
501 {
502 AddSummarySceneObjectReport(sb, so);
503 }
432 504
433 return sb; 505 return sb;
434 } 506 }
435 507
436 private StringBuilder AddScenePartReport(StringBuilder sb, SceneObjectPart sop) 508 private StringBuilder AddSummarySceneObjectReport(StringBuilder sb, SceneObjectGroup so)
437 { 509 {
438 sb.AppendFormat("Name: {0}\n", sop.Name); 510 ConsoleDisplayList cdl = new ConsoleDisplayList();
439 sb.AppendFormat("Description: {0}\n", sop.Description); 511 cdl.AddRow("Name", so.Name);
440 sb.AppendFormat("Location: {0} @ {1}\n", sop.AbsolutePosition, sop.ParentGroup.Scene.RegionInfo.RegionName); 512 cdl.AddRow("Descrition", so.Description);
441 sb.AppendFormat("Parent: {0}", 513 cdl.AddRow("Local ID", so.LocalId);
442 sop.IsRoot ? "Is Root\n" : string.Format("{0} {1}\n", sop.ParentGroup.Name, sop.ParentGroup.UUID)); 514 cdl.AddRow("UUID", so.UUID);
443 sb.AppendFormat("Link number: {0}\n", sop.LinkNum); 515 cdl.AddRow("Location", string.Format("{0} @ {1}", so.AbsolutePosition, so.Scene.Name));
444 sb.AppendFormat("Flags: {0}\n", sop.Flags); 516 cdl.AddRow("Parts", so.PrimCount);
517 cdl.AddRow("Flags", so.RootPart.Flags);
518
519 return sb.Append(cdl.ToString());
520 }
445 521
446 return sb; 522 /// <summary>
523 /// Append a scene object part report to an input StringBuilder
524 /// </summary>
525 /// <returns></returns>
526 /// <param name='sb'></param>
527 /// <param name='sop'</param>
528 /// <param name='showFull'>
529 /// If true then information on each inventory item will be shown.
530 /// If false then only summary inventory information is shown.
531 /// </param>
532 private StringBuilder AddScenePartReport(StringBuilder sb, SceneObjectPart sop, bool showFull)
533 {
534 ConsoleDisplayList cdl = new ConsoleDisplayList();
535 cdl.AddRow("Name", sop.Name);
536 cdl.AddRow("Description", sop.Description);
537 cdl.AddRow("Local ID", sop.LocalId);
538 cdl.AddRow("UUID", sop.UUID);
539 cdl.AddRow("Location", string.Format("{0} @ {1}", sop.AbsolutePosition, sop.ParentGroup.Scene.Name));
540 cdl.AddRow(
541 "Parent",
542 sop.IsRoot ? "Is Root" : string.Format("{0} {1}", sop.ParentGroup.Name, sop.ParentGroup.UUID));
543 cdl.AddRow("Link number", sop.LinkNum);
544 cdl.AddRow("Flags", sop.Flags);
545
546 object itemsOutput;
547 if (showFull)
548 {
549 StringBuilder itemsSb = new StringBuilder("\n");
550 itemsOutput = AddScenePartItemsReport(itemsSb, sop.Inventory).ToString();
551 }
552 else
553 {
554 itemsOutput = sop.Inventory.Count;
555 }
556
557
558 cdl.AddRow("Items", itemsOutput);
559
560 return sb.Append(cdl.ToString());
561 }
562
563 private StringBuilder AddScenePartItemsReport(StringBuilder sb, IEntityInventory inv)
564 {
565 ConsoleDisplayTable cdt = new ConsoleDisplayTable();
566 cdt.Indent = 2;
567
568 cdt.AddColumn("Name", 50);
569 cdt.AddColumn("Type", 12);
570 cdt.AddColumn("Running", 7);
571 cdt.AddColumn("Item UUID", 36);
572 cdt.AddColumn("Asset UUID", 36);
573
574 foreach (TaskInventoryItem item in inv.GetInventoryItems())
575 cdt.AddRow(
576 item.Name,
577 ((InventoryType)item.InvType).ToString(),
578 (InventoryType)item.InvType == InventoryType.LSL ? item.ScriptRunning.ToString() : "n/a",
579 item.ItemID.ToString(),
580 item.AssetID.ToString());
581
582 return sb.Append(cdt.ToString());
447 } 583 }
448 584
449 private void HandleDeleteObject(string module, string[] cmd) 585 private void HandleDeleteObject(string module, string[] cmd)
@@ -557,6 +693,10 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
557 693
558 break; 694 break;
559 695
696 case "pos":
697 deletes = GetDeleteCandidatesByPos(module, cmd);
698 break;
699
560 default: 700 default:
561 m_console.OutputFormat("Unrecognized mode {0}", mode); 701 m_console.OutputFormat("Unrecognized mode {0}", mode);
562 return; 702 return;
@@ -571,7 +711,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
571 string.Format( 711 string.Format(
572 "Are you sure that you want to delete {0} objects from {1}", 712 "Are you sure that you want to delete {0} objects from {1}",
573 deletes.Count, m_scene.RegionInfo.RegionName), 713 deletes.Count, m_scene.RegionInfo.RegionName),
574 "n"); 714 "y/N");
575 715
576 if (response.ToLower() != "y") 716 if (response.ToLower() != "y")
577 { 717 {
@@ -593,9 +733,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
593 733
594 private List<SceneObjectGroup> GetDeleteCandidatesByName(string module, string[] cmdparams) 734 private List<SceneObjectGroup> GetDeleteCandidatesByName(string module, string[] cmdparams)
595 { 735 {
596 if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
597 return null;
598
599 bool useRegex = false; 736 bool useRegex = false;
600 OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null ); 737 OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null );
601 738
@@ -629,5 +766,52 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
629 766
630 return sceneObjects; 767 return sceneObjects;
631 } 768 }
769
770 /// <summary>
771 /// Get scene object delete candidates by position
772 /// </summary>
773 /// <param name='module'></param>
774 /// <param name='cmdparams'></param>
775 /// <returns>null if parsing failed on one of the arguments, otherwise a list of objects to delete. If there
776 /// are no objects to delete then the list will be empty./returns>
777 private List<SceneObjectGroup> GetDeleteCandidatesByPos(string module, string[] cmdparams)
778 {
779 if (cmdparams.Length < 5)
780 {
781 m_console.OutputFormat("Usage: delete object pos <start-coord> to <end-coord>");
782 return null;
783 }
784
785 Vector3 startVector, endVector;
786
787 if (!TryParseVectorRange(cmdparams.Skip(3).Take(3), out startVector, out endVector))
788 return null;
789
790 return m_scene.GetSceneObjectGroups().FindAll(
791 so => !so.IsAttachment && Util.IsInsideBox(so.AbsolutePosition, startVector, endVector));
792 }
793
794 private bool TryParseVectorRange(IEnumerable<string> rawComponents, out Vector3 startVector, out Vector3 endVector)
795 {
796 string rawConsoleStartVector = rawComponents.Take(1).Single();
797
798 if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector))
799 {
800 m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector);
801 endVector = Vector3.Zero;
802
803 return false;
804 }
805
806 string rawConsoleEndVector = rawComponents.Skip(1).Take(1).Single();
807
808 if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector))
809 {
810 m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector);
811 return false;
812 }
813
814 return true;
815 }
632 } 816 }
633} \ No newline at end of file 817} \ No newline at end of file
diff --git a/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs b/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs
index 8d62847..c457b2f 100644
--- a/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs
+++ b/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs
@@ -268,17 +268,25 @@ namespace OpenSim.Region.Framework.Interfaces
268 void ApplyGodPermissions(uint perms); 268 void ApplyGodPermissions(uint perms);
269 269
270 /// <summary> 270 /// <summary>
271 /// Number of items in this inventory.
272 /// </summary>
273 int Count { get; }
274
275 /// <summary>
271 /// Returns true if this inventory contains any scripts 276 /// Returns true if this inventory contains any scripts
272 /// </summary></returns> 277 /// </summary></returns>
273 bool ContainsScripts(); 278 bool ContainsScripts();
274 279
275 /// <summary> 280 /// <summary>
276 /// Returns the count of scripts contained 281 /// Number of scripts in this inventory.
277 /// </summary></returns> 282 /// </summary>
283 /// <remarks>
284 /// Includes both running and non running scripts.
285 /// </remarks>
278 int ScriptCount(); 286 int ScriptCount();
279 287
280 /// <summary> 288 /// <summary>
281 /// Returns the count of running scripts contained 289 /// Number of running scripts in this inventory.
282 /// </summary></returns> 290 /// </summary></returns>
283 int RunningScriptCount(); 291 int RunningScriptCount();
284 292
diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs
index a8ff218..4c49b71 100644
--- a/OpenSim/Region/Framework/Scenes/EventManager.cs
+++ b/OpenSim/Region/Framework/Scenes/EventManager.cs
@@ -121,13 +121,21 @@ namespace OpenSim.Region.Framework.Scenes
121 /// </summary> 121 /// </summary>
122 /// <remarks> 122 /// <remarks>
123 /// This is triggered for both child and root agent client connections. 123 /// This is triggered for both child and root agent client connections.
124 ///
124 /// Triggered before OnClientLogin. 125 /// Triggered before OnClientLogin.
126 ///
127 /// This is triggered under per-agent lock. So if you want to perform any long-running operations, please
128 /// do this on a separate thread.
125 /// </remarks> 129 /// </remarks>
126 public event OnNewClientDelegate OnNewClient; 130 public event OnNewClientDelegate OnNewClient;
127 131
128 /// <summary> 132 /// <summary>
129 /// Fired if the client entering this sim is doing so as a new login 133 /// Fired if the client entering this sim is doing so as a new login
130 /// </summary> 134 /// </summary>
135 /// <remarks>
136 /// This is triggered under per-agent lock. So if you want to perform any long-running operations, please
137 /// do this on a separate thread.
138 /// </remarks>
131 public event Action<IClientAPI> OnClientLogin; 139 public event Action<IClientAPI> OnClientLogin;
132 140
133 public delegate void OnNewPresenceDelegate(ScenePresence presence); 141 public delegate void OnNewPresenceDelegate(ScenePresence presence);
@@ -149,6 +157,9 @@ namespace OpenSim.Region.Framework.Scenes
149 /// <remarks> 157 /// <remarks>
150 /// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.AddNewClient"/> which is used by both 158 /// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.AddNewClient"/> which is used by both
151 /// <see cref="OpenSim.Framework.PresenceType.User">users</see> and <see cref="OpenSim.Framework.PresenceType.Npc">NPCs</see> 159 /// <see cref="OpenSim.Framework.PresenceType.User">users</see> and <see cref="OpenSim.Framework.PresenceType.Npc">NPCs</see>
160 ///
161 /// Triggered under per-agent lock. So if you want to perform any long-running operations, please
162 /// do this on a separate thread.
152 /// </remarks> 163 /// </remarks>
153 public event OnRemovePresenceDelegate OnRemovePresence; 164 public event OnRemovePresenceDelegate OnRemovePresence;
154 165
@@ -425,6 +436,9 @@ namespace OpenSim.Region.Framework.Scenes
425 /// </summary> 436 /// </summary>
426 /// <remarks> 437 /// <remarks>
427 /// At the point of firing, the scene still contains the client's scene presence. 438 /// At the point of firing, the scene still contains the client's scene presence.
439 ///
440 /// This is triggered under per-agent lock. So if you want to perform any long-running operations, please
441 /// do this on a separate thread.
428 /// </remarks> 442 /// </remarks>
429 public event ClientClosed OnClientClosed; 443 public event ClientClosed OnClientClosed;
430 444
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index 671feda..5f45529 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -80,6 +80,11 @@ namespace OpenSim.Region.Framework.Scenes
80 public SynchronizeSceneHandler SynchronizeScene; 80 public SynchronizeSceneHandler SynchronizeScene;
81 81
82 /// <summary> 82 /// <summary>
83 /// Used to prevent simultaneous calls to RemoveClient() for the same agent from interfering with each other.
84 /// </summary>
85 private object m_removeClientLock = new object();
86
87 /// <summary>
83 /// Statistical information for this scene. 88 /// Statistical information for this scene.
84 /// </summary> 89 /// </summary>
85 public SimStatsReporter StatsReporter { get; private set; } 90 public SimStatsReporter StatsReporter { get; private set; }
@@ -301,6 +306,31 @@ namespace OpenSim.Region.Framework.Scenes
301 } 306 }
302 private volatile bool m_shuttingDown; 307 private volatile bool m_shuttingDown;
303 308
309 /// <summary>
310 /// Is the scene active?
311 /// </summary>
312 /// <remarks>
313 /// If false, maintenance and update loops are not being run. Updates can still be triggered manually if
314 /// the scene is not active.
315 /// </remarks>
316 public bool Active
317 {
318 get { return m_active; }
319 set
320 {
321 if (value)
322 {
323 if (!m_active)
324 Start();
325 }
326 else
327 {
328 m_active = false;
329 }
330 }
331 }
332 private volatile bool m_active;
333
304// private int m_lastUpdate; 334// private int m_lastUpdate;
305// private bool m_firstHeartbeat = true; 335// private bool m_firstHeartbeat = true;
306 336
@@ -1154,6 +1184,14 @@ namespace OpenSim.Region.Framework.Scenes
1154 1184
1155 public void SetSceneCoreDebug(Dictionary<string, string> options) 1185 public void SetSceneCoreDebug(Dictionary<string, string> options)
1156 { 1186 {
1187 if (options.ContainsKey("active"))
1188 {
1189 bool active;
1190
1191 if (bool.TryParse(options["active"], out active))
1192 Active = active;
1193 }
1194
1157 if (options.ContainsKey("scripting")) 1195 if (options.ContainsKey("scripting"))
1158 { 1196 {
1159 bool enableScripts = true; 1197 bool enableScripts = true;
@@ -1293,6 +1331,8 @@ namespace OpenSim.Region.Framework.Scenes
1293 /// </summary> 1331 /// </summary>
1294 public void Start() 1332 public void Start()
1295 { 1333 {
1334 m_active = true;
1335
1296// m_log.DebugFormat("[SCENE]: Starting Heartbeat timer for {0}", RegionInfo.RegionName); 1336// m_log.DebugFormat("[SCENE]: Starting Heartbeat timer for {0}", RegionInfo.RegionName);
1297 1337
1298 //m_heartbeatTimer.Enabled = true; 1338 //m_heartbeatTimer.Enabled = true;
@@ -1334,7 +1374,7 @@ namespace OpenSim.Region.Framework.Scenes
1334 #region Update Methods 1374 #region Update Methods
1335 1375
1336 /// <summary> 1376 /// <summary>
1337 /// Performs per-frame updates regularly 1377 /// Activate the various loops necessary to continually update the scene.
1338 /// </summary> 1378 /// </summary>
1339 private void Heartbeat() 1379 private void Heartbeat()
1340 { 1380 {
@@ -1391,7 +1431,7 @@ namespace OpenSim.Region.Framework.Scenes
1391 List<Vector3> coarseLocations; 1431 List<Vector3> coarseLocations;
1392 List<UUID> avatarUUIDs; 1432 List<UUID> avatarUUIDs;
1393 1433
1394 while (!m_shuttingDown && (endRun == null || MaintenanceRun < endRun)) 1434 while (!m_shuttingDown && ((endRun == null && Active) || MaintenanceRun < endRun))
1395 { 1435 {
1396 runtc = Util.EnvironmentTickCount(); 1436 runtc = Util.EnvironmentTickCount();
1397 ++MaintenanceRun; 1437 ++MaintenanceRun;
@@ -1450,7 +1490,7 @@ namespace OpenSim.Region.Framework.Scenes
1450 int previousFrameTick, tmpMS; 1490 int previousFrameTick, tmpMS;
1451 int maintc = Util.EnvironmentTickCount(); 1491 int maintc = Util.EnvironmentTickCount();
1452 1492
1453 while (!m_shuttingDown && (endFrame == null || Frame < endFrame)) 1493 while (!m_shuttingDown && ((endFrame == null && Active) || Frame < endFrame))
1454 { 1494 {
1455 ++Frame; 1495 ++Frame;
1456 1496
@@ -2709,69 +2749,89 @@ namespace OpenSim.Region.Framework.Scenes
2709 2749
2710 public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) 2750 public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type)
2711 { 2751 {
2752 ScenePresence sp;
2753 bool vialogin;
2754
2712 // Validation occurs in LLUDPServer 2755 // Validation occurs in LLUDPServer
2756 //
2757 // XXX: A race condition exists here where two simultaneous calls to AddNewClient can interfere with
2758 // each other. In practice, this does not currently occur in the code.
2713 AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode); 2759 AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode);
2714 2760
2715 bool vialogin 2761 // We lock here on AgentCircuitData to prevent a race condition between the thread adding a new connection
2716 = (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 2762 // and a simultaneous one that removes it (as can happen if the client is closed at a particular point
2717 || (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0; 2763 // whilst connecting).
2718 2764 //
2719// CheckHeartbeat(); 2765 // It would be easier to lock across all NewUserConnection(), AddNewClient() and
2720 2766 // RemoveClient() calls for all agents, but this would allow a slow call (e.g. because of slow service
2721 ScenePresence sp = GetScenePresence(client.AgentId); 2767 // response in some module listening to AddNewClient()) from holding up unrelated agent calls.
2722 2768 //
2723 // XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this 2769 // In practice, the lock (this) in LLUDPServer.AddNewClient() currently lock across all
2724 // could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause 2770 // AddNewClient() operations (though not other ops).
2725 // other problems, and possible the code calling AddNewClient() should ensure that no client is already 2771 // In the future this can be relieved once locking per agent (not necessarily on AgentCircuitData) is improved.
2726 // connected. 2772 lock (aCircuit)
2727 if (sp == null) 2773 {
2728 { 2774 vialogin
2729 m_log.DebugFormat( 2775 = (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0
2730 "[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}", 2776 || (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0;
2731 client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos); 2777
2732 2778 // CheckHeartbeat();
2733 m_clientManager.Add(client); 2779
2734 SubscribeToClientEvents(client); 2780 sp = GetScenePresence(client.AgentId);
2735
2736 sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
2737 m_eventManager.TriggerOnNewPresence(sp);
2738
2739 sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
2740 2781
2741 // The first agent upon login is a root agent by design. 2782 // XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this
2742 // For this agent we will have to rez the attachments. 2783 // could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause
2743 // All other AddNewClient calls find aCircuit.child to be true. 2784 // other problems, and possible the code calling AddNewClient() should ensure that no client is already
2744 if (aCircuit.child == false) 2785 // connected.
2786 if (sp == null)
2745 { 2787 {
2746 // We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to 2788 m_log.DebugFormat(
2747 // start the scripts again (since this is done in RezAttachments()). 2789 "[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}",
2748 // XXX: This is convoluted. 2790 client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos);
2749 sp.IsChildAgent = false; 2791
2750 2792 m_clientManager.Add(client);
2751 if (AttachmentsModule != null) 2793 SubscribeToClientEvents(client);
2752 Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); }); 2794
2795 sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
2796 m_eventManager.TriggerOnNewPresence(sp);
2797
2798 sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
2799
2800 // The first agent upon login is a root agent by design.
2801 // For this agent we will have to rez the attachments.
2802 // All other AddNewClient calls find aCircuit.child to be true.
2803 if (aCircuit.child == false)
2804 {
2805 // We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to
2806 // start the scripts again (since this is done in RezAttachments()).
2807 // XXX: This is convoluted.
2808 sp.IsChildAgent = false;
2809
2810 if (AttachmentsModule != null)
2811 Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); });
2812 }
2753 } 2813 }
2754 } 2814 else
2755 else 2815 {
2756 { 2816 m_log.WarnFormat(
2757 m_log.WarnFormat( 2817 "[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence",
2758 "[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence", 2818 sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName);
2759 sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName); 2819 }
2760 } 2820
2821 // We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
2822 // client is for a root or child agent.
2823 client.SceneAgent = sp;
2761 2824
2762 // We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the 2825 // Cache the user's name
2763 // client is for a root or child agent. 2826 CacheUserName(sp, aCircuit);
2764 client.SceneAgent = sp; 2827
2828 EventManager.TriggerOnNewClient(client);
2829 if (vialogin)
2830 EventManager.TriggerOnClientLogin(client);
2831 }
2765 2832
2766 m_LastLogin = Util.EnvironmentTickCount(); 2833 m_LastLogin = Util.EnvironmentTickCount();
2767 2834
2768 // Cache the user's name
2769 CacheUserName(sp, aCircuit);
2770
2771 EventManager.TriggerOnNewClient(client);
2772 if (vialogin)
2773 EventManager.TriggerOnClientLogin(client);
2774
2775 return sp; 2835 return sp;
2776 } 2836 }
2777 2837
@@ -3300,109 +3360,129 @@ namespace OpenSim.Region.Framework.Scenes
3300 { 3360 {
3301// CheckHeartbeat(); 3361// CheckHeartbeat();
3302 bool isChildAgent = false; 3362 bool isChildAgent = false;
3303 ScenePresence avatar = GetScenePresence(agentID); 3363 AgentCircuitData acd;
3304 3364
3305 if (avatar == null) 3365 lock (m_removeClientLock)
3306 { 3366 {
3307 m_log.WarnFormat( 3367 acd = m_authenticateHandler.GetAgentCircuitData(agentID);
3308 "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID);
3309
3310 return;
3311 }
3312 3368
3313 try 3369 if (acd == null)
3314 {
3315 isChildAgent = avatar.IsChildAgent;
3316
3317 m_log.DebugFormat(
3318 "[SCENE]: Removing {0} agent {1} {2} from {3}",
3319 (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName);
3320
3321 // Don't do this to root agents, it's not nice for the viewer
3322 if (closeChildAgents && isChildAgent)
3323 { 3370 {
3324 // Tell a single agent to disconnect from the region. 3371 m_log.ErrorFormat("[SCENE]: No agent circuit found for {0}, aborting Scene.RemoveClient", agentID);
3325 IEventQueue eq = RequestModuleInterface<IEventQueue>(); 3372 return;
3326 if (eq != null)
3327 {
3328 eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID);
3329 }
3330 else
3331 {
3332 avatar.ControllingClient.SendShutdownConnectionNotice();
3333 }
3334 } 3373 }
3335 3374 else
3336 // Only applies to root agents.
3337 if (avatar.ParentID != 0)
3338 { 3375 {
3339 avatar.StandUp(); 3376 // We remove the acd up here to avoid later raec conditions if two RemoveClient() calls occurred
3377 // simultaneously.
3378 m_authenticateHandler.RemoveCircuit(acd.circuitcode);
3340 } 3379 }
3380 }
3341 3381
3342 m_sceneGraph.removeUserCount(!isChildAgent); 3382 lock (acd)
3343 3383 {
3344 // TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop 3384 ScenePresence avatar = GetScenePresence(agentID);
3345 // unnecessary operations. This should go away once NPCs have no accompanying IClientAPI 3385
3346 if (closeChildAgents && CapsModule != null) 3386 if (avatar == null)
3347 CapsModule.RemoveCaps(agentID);
3348
3349 // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever
3350 // this method is doing is HORRIBLE!!!
3351 avatar.Scene.NeedSceneCacheClear(avatar.UUID);
3352
3353 if (closeChildAgents && !isChildAgent)
3354 { 3387 {
3355 List<ulong> regions = avatar.KnownRegionHandles; 3388 m_log.WarnFormat(
3356 regions.Remove(RegionInfo.RegionHandle); 3389 "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID);
3357 m_sceneGridService.SendCloseChildAgentConnections(agentID, regions); 3390
3391 return;
3358 } 3392 }
3359 3393
3360 m_eventManager.TriggerClientClosed(agentID, this); 3394 try
3361 m_eventManager.TriggerOnRemovePresence(agentID);
3362
3363 if (!isChildAgent)
3364 { 3395 {
3365 if (AttachmentsModule != null) 3396 isChildAgent = avatar.IsChildAgent;
3397
3398 m_log.DebugFormat(
3399 "[SCENE]: Removing {0} agent {1} {2} from {3}",
3400 (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName);
3401
3402 // Don't do this to root agents, it's not nice for the viewer
3403 if (closeChildAgents && isChildAgent)
3366 { 3404 {
3367 AttachmentsModule.DeRezAttachments(avatar); 3405 // Tell a single agent to disconnect from the region.
3406 IEventQueue eq = RequestModuleInterface<IEventQueue>();
3407 if (eq != null)
3408 {
3409 eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID);
3410 }
3411 else
3412 {
3413 avatar.ControllingClient.SendShutdownConnectionNotice();
3414 }
3368 } 3415 }
3369 3416
3370 ForEachClient( 3417 // Only applies to root agents.
3371 delegate(IClientAPI client) 3418 if (avatar.ParentID != 0)
3419 {
3420 avatar.StandUp();
3421 }
3422
3423 m_sceneGraph.removeUserCount(!isChildAgent);
3424
3425 // TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop
3426 // unnecessary operations. This should go away once NPCs have no accompanying IClientAPI
3427 if (closeChildAgents && CapsModule != null)
3428 CapsModule.RemoveCaps(agentID);
3429
3430 // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever
3431 // this method is doing is HORRIBLE!!!
3432 avatar.Scene.NeedSceneCacheClear(avatar.UUID);
3433
3434 if (closeChildAgents && !isChildAgent)
3435 {
3436 List<ulong> regions = avatar.KnownRegionHandles;
3437 regions.Remove(RegionInfo.RegionHandle);
3438 m_sceneGridService.SendCloseChildAgentConnections(agentID, regions);
3439 }
3440
3441 m_eventManager.TriggerClientClosed(agentID, this);
3442 m_eventManager.TriggerOnRemovePresence(agentID);
3443
3444 if (!isChildAgent)
3445 {
3446 if (AttachmentsModule != null)
3372 { 3447 {
3373 //We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway 3448 AttachmentsModule.DeRezAttachments(avatar);
3374 try { client.SendKillObject(avatar.RegionHandle, new List<uint> { avatar.LocalId }); } 3449 }
3375 catch (NullReferenceException) { }
3376 });
3377 }
3378
3379 // It's possible for child agents to have transactions if changes are being made cross-border.
3380 if (AgentTransactionsModule != null)
3381 AgentTransactionsModule.RemoveAgentAssetTransactions(agentID);
3382 3450
3383 m_authenticateHandler.RemoveCircuit(avatar.ControllingClient.CircuitCode); 3451 ForEachClient(
3384 } 3452 delegate(IClientAPI client)
3385 catch (Exception e) 3453 {
3386 { 3454 //We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway
3387 m_log.Error( 3455 try { client.SendKillObject(avatar.RegionHandle, new List<uint> { avatar.LocalId }); }
3388 string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e); 3456 catch (NullReferenceException) { }
3389 } 3457 });
3390 finally 3458 }
3391 {
3392 try
3393 {
3394 // Always clean these structures up so that any failure above doesn't cause them to remain in the
3395 // scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering
3396 // the same cleanup exception continually.
3397 m_sceneGraph.RemoveScenePresence(agentID);
3398 m_clientManager.Remove(agentID);
3399 3459
3400 avatar.Close(); 3460 // It's possible for child agents to have transactions if changes are being made cross-border.
3461 if (AgentTransactionsModule != null)
3462 AgentTransactionsModule.RemoveAgentAssetTransactions(agentID);
3401 } 3463 }
3402 catch (Exception e) 3464 catch (Exception e)
3403 { 3465 {
3404 m_log.Error( 3466 m_log.Error(
3405 string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e); 3467 string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e);
3468 }
3469 finally
3470 {
3471 try
3472 {
3473 // Always clean these structures up so that any failure above doesn't cause them to remain in the
3474 // scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering
3475 // the same cleanup exception continually.
3476 m_sceneGraph.RemoveScenePresence(agentID);
3477 m_clientManager.Remove(agentID);
3478
3479 avatar.Close();
3480 }
3481 catch (Exception e)
3482 {
3483 m_log.Error(
3484 string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e);
3485 }
3406 } 3486 }
3407 } 3487 }
3408 3488
@@ -3461,11 +3541,9 @@ namespace OpenSim.Region.Framework.Scenes
3461 3541
3462 /// <summary> 3542 /// <summary>
3463 /// Do the work necessary to initiate a new user connection for a particular scene. 3543 /// Do the work necessary to initiate a new user connection for a particular scene.
3464 /// At the moment, this consists of setting up the caps infrastructure
3465 /// The return bool should allow for connections to be refused, but as not all calling paths
3466 /// take proper notice of it let, we allowed banned users in still.
3467 /// </summary> 3544 /// </summary>
3468 /// <param name="agent">CircuitData of the agent who is connecting</param> 3545 /// <param name="agent">CircuitData of the agent who is connecting</param>
3546 /// <param name="teleportFlags"></param>
3469 /// <param name="reason">Outputs the reason for the false response on this string</param> 3547 /// <param name="reason">Outputs the reason for the false response on this string</param>
3470 /// <returns>True if the region accepts this agent. False if it does not. False will 3548 /// <returns>True if the region accepts this agent. False if it does not. False will
3471 /// also return a reason.</returns> 3549 /// also return a reason.</returns>
@@ -3476,10 +3554,20 @@ namespace OpenSim.Region.Framework.Scenes
3476 3554
3477 /// <summary> 3555 /// <summary>
3478 /// Do the work necessary to initiate a new user connection for a particular scene. 3556 /// Do the work necessary to initiate a new user connection for a particular scene.
3479 /// At the moment, this consists of setting up the caps infrastructure 3557 /// </summary>
3558 /// <remarks>
3559 /// The return bool should allow for connections to be refused, but as not all calling paths
3560 /// take proper notice of it yet, we still allowed banned users in.
3561 ///
3562 /// At the moment this method consists of setting up the caps infrastructure
3480 /// The return bool should allow for connections to be refused, but as not all calling paths 3563 /// The return bool should allow for connections to be refused, but as not all calling paths
3481 /// take proper notice of it let, we allowed banned users in still. 3564 /// take proper notice of it let, we allowed banned users in still.
3482 /// </summary> 3565 ///
3566 /// This method is called by the login service (in the case of login) or another simulator (in the case of region
3567 /// cross or teleport) to initiate the connection. It is not triggered by the viewer itself - the connection
3568 /// is activated later when the viewer sends the initial UseCircuitCodePacket UDP packet (in the case of
3569 /// the LLUDP stack).
3570 /// </remarks>
3483 /// <param name="agent">CircuitData of the agent who is connecting</param> 3571 /// <param name="agent">CircuitData of the agent who is connecting</param>
3484 /// <param name="reason">Outputs the reason for the false response on this string</param> 3572 /// <param name="reason">Outputs the reason for the false response on this string</param>
3485 /// <param name="requirePresenceLookup">True for normal presence. False for NPC 3573 /// <param name="requirePresenceLookup">True for normal presence. False for NPC
@@ -3564,88 +3652,97 @@ namespace OpenSim.Region.Framework.Scenes
3564 agent.firstname, agent.lastname, agent.Viewer); 3652 agent.firstname, agent.lastname, agent.Viewer);
3565 reason = "Access denied, your viewer is banned by the region owner"; 3653 reason = "Access denied, your viewer is banned by the region owner";
3566 return false; 3654 return false;
3567 }
3568
3569
3570 ScenePresence sp = GetScenePresence(agent.AgentID);
3571
3572 if (sp != null && !sp.IsChildAgent)
3573 {
3574 // We have a zombie from a crashed session.
3575 // Or the same user is trying to be root twice here, won't work.
3576 // Kill it.
3577 m_log.WarnFormat(
3578 "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.",
3579 sp.Name, sp.UUID, RegionInfo.RegionName);
3580
3581 sp.ControllingClient.Close(true);
3582 sp = null;
3583 } 3655 }
3584 3656
3585 ILandObject land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); 3657 ILandObject land;
3586 3658
3587 //On login test land permisions 3659 lock (agent)
3588 if (vialogin)
3589 { 3660 {
3590 if (land != null && !TestLandRestrictions(agent, land, out reason)) 3661 ScenePresence sp = GetScenePresence(agent.AgentID);
3662
3663 if (sp != null && !sp.IsChildAgent)
3591 { 3664 {
3592 return false; 3665 // We have a zombie from a crashed session.
3666 // Or the same user is trying to be root twice here, won't work.
3667 // Kill it.
3668 m_log.WarnFormat(
3669 "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.",
3670 sp.Name, sp.UUID, RegionInfo.RegionName);
3671
3672 sp.ControllingClient.Close(true);
3673 sp = null;
3593 } 3674 }
3594 } 3675
3595 3676 land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y);
3596 if (sp == null) // We don't have an [child] agent here already 3677
3597 { 3678 //On login test land permisions
3598 if (requirePresenceLookup) 3679 if (vialogin)
3599 { 3680 {
3600 try 3681 if (land != null && !TestLandRestrictions(agent, land, out reason))
3601 { 3682 {
3602 if (!VerifyUserPresence(agent, out reason))
3603 return false;
3604 } catch (Exception e)
3605 {
3606 m_log.ErrorFormat(
3607 "[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace);
3608 return false; 3683 return false;
3609 } 3684 }
3610 } 3685 }
3611 3686
3612 try 3687 if (sp == null) // We don't have an [child] agent here already
3613 {
3614 if (!AuthorizeUser(agent, out reason))
3615 return false;
3616 } catch (Exception e)
3617 {
3618 m_log.ErrorFormat(
3619 "[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace);
3620 return false;
3621 }
3622
3623 m_log.InfoFormat(
3624 "[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})",
3625 RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname,
3626 agent.AgentID, agent.circuitcode);
3627
3628 if (CapsModule != null)
3629 { 3688 {
3630 CapsModule.SetAgentCapsSeeds(agent); 3689 if (requirePresenceLookup)
3631 CapsModule.CreateCaps(agent.AgentID); 3690 {
3632 } 3691 try
3633 } else 3692 {
3634 { 3693 if (!VerifyUserPresence(agent, out reason))
3635 // Let the SP know how we got here. This has a lot of interesting 3694 return false;
3636 // uses down the line. 3695 }
3637 sp.TeleportFlags = (TPFlags)teleportFlags; 3696 catch (Exception e)
3697 {
3698 m_log.ErrorFormat(
3699 "[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace);
3638 3700
3639 if (sp.IsChildAgent) 3701 return false;
3640 { 3702 }
3641 m_log.DebugFormat( 3703 }
3642 "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", 3704
3643 agent.AgentID, RegionInfo.RegionName); 3705 try
3706 {
3707 if (!AuthorizeUser(agent, out reason))
3708 return false;
3709 }
3710 catch (Exception e)
3711 {
3712 m_log.ErrorFormat(
3713 "[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace);
3644 3714
3645 sp.AdjustKnownSeeds(); 3715 return false;
3646 3716 }
3717
3718 m_log.InfoFormat(
3719 "[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})",
3720 RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname,
3721 agent.AgentID, agent.circuitcode);
3722
3647 if (CapsModule != null) 3723 if (CapsModule != null)
3724 {
3648 CapsModule.SetAgentCapsSeeds(agent); 3725 CapsModule.SetAgentCapsSeeds(agent);
3726 CapsModule.CreateCaps(agent.AgentID);
3727 }
3728 }
3729 else
3730 {
3731 // Let the SP know how we got here. This has a lot of interesting
3732 // uses down the line.
3733 sp.TeleportFlags = (TPFlags)teleportFlags;
3734
3735 if (sp.IsChildAgent)
3736 {
3737 m_log.DebugFormat(
3738 "[SCENE]: Adjusting known seeds for existing agent {0} in {1}",
3739 agent.AgentID, RegionInfo.RegionName);
3740
3741 sp.AdjustKnownSeeds();
3742
3743 if (CapsModule != null)
3744 CapsModule.SetAgentCapsSeeds(agent);
3745 }
3649 } 3746 }
3650 } 3747 }
3651 3748
@@ -4047,8 +4144,9 @@ namespace OpenSim.Region.Framework.Scenes
4047 return false; 4144 return false;
4048 } 4145 }
4049 4146
4050 // We have to wait until the viewer contacts this region after receiving EAC. 4147 // We have to wait until the viewer contacts this region
4051 // That calls AddNewClient, which finally creates the ScenePresence 4148 // after receiving the EnableSimulator HTTP Event Queue message. This triggers the viewer to send
4149 // a UseCircuitCode packet which in turn calls AddNewClient which finally creates the ScenePresence.
4052 ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID); 4150 ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID);
4053 4151
4054 if (childAgentUpdate != null) 4152 if (childAgentUpdate != null)
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
index 45bbbda..1fa6a75 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
@@ -3432,6 +3432,7 @@ namespace OpenSim.Region.Framework.Scenes
3432 /// <remarks> 3432 /// <remarks>
3433 /// When the physics engine has finished with it, the sculpt data is discarded to save memory. 3433 /// When the physics engine has finished with it, the sculpt data is discarded to save memory.
3434 /// </remarks> 3434 /// </remarks>
3435/*
3435 public void CheckSculptAndLoad() 3436 public void CheckSculptAndLoad()
3436 { 3437 {
3437 if (IsDeleted) 3438 if (IsDeleted)
@@ -3447,7 +3448,7 @@ namespace OpenSim.Region.Framework.Scenes
3447 for (int i = 0; i < parts.Length; i++) 3448 for (int i = 0; i < parts.Length; i++)
3448 parts[i].CheckSculptAndLoad(); 3449 parts[i].CheckSculptAndLoad();
3449 } 3450 }
3450 3451*/
3451 /// <summary> 3452 /// <summary>
3452 /// Set the user group to which this scene object belongs. 3453 /// Set the user group to which this scene object belongs.
3453 /// </summary> 3454 /// </summary>
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
index 199526e..27ef4c9 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
@@ -1014,9 +1014,9 @@ namespace OpenSim.Region.Framework.Scenes
1014 { 1014 {
1015 actor.Size = m_shape.Scale; 1015 actor.Size = m_shape.Scale;
1016 1016
1017 if (Shape.SculptEntry) 1017// if (Shape.SculptEntry)
1018 CheckSculptAndLoad(); 1018// CheckSculptAndLoad();
1019 else 1019// else
1020 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor); 1020 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor);
1021 } 1021 }
1022 } 1022 }
@@ -1620,12 +1620,13 @@ namespace OpenSim.Region.Framework.Scenes
1620 1620
1621 if (userExposed) 1621 if (userExposed)
1622 { 1622 {
1623/*
1623 if (dupe.m_shape.SculptEntry && dupe.m_shape.SculptTexture != UUID.Zero) 1624 if (dupe.m_shape.SculptEntry && dupe.m_shape.SculptTexture != UUID.Zero)
1624 { 1625 {
1625 ParentGroup.Scene.AssetService.Get( 1626 ParentGroup.Scene.AssetService.Get(
1626 dupe.m_shape.SculptTexture.ToString(), dupe, dupe.AssetReceived); 1627 dupe.m_shape.SculptTexture.ToString(), dupe, dupe.AssetReceived);
1627 } 1628 }
1628 1629*/
1629 bool UsePhysics = ((dupe.Flags & PrimFlags.Physics) != 0); 1630 bool UsePhysics = ((dupe.Flags & PrimFlags.Physics) != 0);
1630 dupe.DoPhysicsPropertyUpdate(UsePhysics, true); 1631 dupe.DoPhysicsPropertyUpdate(UsePhysics, true);
1631 } 1632 }
@@ -1643,6 +1644,7 @@ namespace OpenSim.Region.Framework.Scenes
1643 /// <param name="id">ID of asset received</param> 1644 /// <param name="id">ID of asset received</param>
1644 /// <param name="sender">Register</param> 1645 /// <param name="sender">Register</param>
1645 /// <param name="asset"></param> 1646 /// <param name="asset"></param>
1647/*
1646 protected void AssetReceived(string id, Object sender, AssetBase asset) 1648 protected void AssetReceived(string id, Object sender, AssetBase asset)
1647 { 1649 {
1648 if (asset != null) 1650 if (asset != null)
@@ -1652,7 +1654,7 @@ namespace OpenSim.Region.Framework.Scenes
1652 "[SCENE OBJECT PART]: Part {0} {1} requested mesh/sculpt data for asset id {2} from asset service but received no data", 1654 "[SCENE OBJECT PART]: Part {0} {1} requested mesh/sculpt data for asset id {2} from asset service but received no data",
1653 Name, UUID, id); 1655 Name, UUID, id);
1654 } 1656 }
1655 1657*/
1656 /// <summary> 1658 /// <summary>
1657 /// Do a physics property update for a NINJA joint. 1659 /// Do a physics property update for a NINJA joint.
1658 /// </summary> 1660 /// </summary>
@@ -1833,9 +1835,9 @@ namespace OpenSim.Region.Framework.Scenes
1833 1835
1834 // If this part is a sculpt then delay the physics update until we've asynchronously loaded the 1836 // If this part is a sculpt then delay the physics update until we've asynchronously loaded the
1835 // mesh data. 1837 // mesh data.
1836 if (Shape.SculptEntry) 1838// if (Shape.SculptEntry)
1837 CheckSculptAndLoad(); 1839// CheckSculptAndLoad();
1838 else 1840// else
1839 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa); 1841 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa);
1840 } 1842 }
1841 } 1843 }
@@ -2511,6 +2513,7 @@ namespace OpenSim.Region.Framework.Scenes
2511 /// Set sculpt and mesh data, and tell the physics engine to process the change. 2513 /// Set sculpt and mesh data, and tell the physics engine to process the change.
2512 /// </summary> 2514 /// </summary>
2513 /// <param name="texture">The mesh itself.</param> 2515 /// <param name="texture">The mesh itself.</param>
2516/*
2514 public void SculptTextureCallback(AssetBase texture) 2517 public void SculptTextureCallback(AssetBase texture)
2515 { 2518 {
2516 if (m_shape.SculptEntry) 2519 if (m_shape.SculptEntry)
@@ -2538,7 +2541,7 @@ namespace OpenSim.Region.Framework.Scenes
2538 } 2541 }
2539 } 2542 }
2540 } 2543 }
2541 2544*/
2542 /// <summary> 2545 /// <summary>
2543 /// Send a full update to the client for the given part 2546 /// Send a full update to the client for the given part
2544 /// </summary> 2547 /// </summary>
@@ -3783,7 +3786,7 @@ namespace OpenSim.Region.Framework.Scenes
3783 public void UpdateExtraParam(ushort type, bool inUse, byte[] data) 3786 public void UpdateExtraParam(ushort type, bool inUse, byte[] data)
3784 { 3787 {
3785 m_shape.ReadInUpdateExtraParam(type, inUse, data); 3788 m_shape.ReadInUpdateExtraParam(type, inUse, data);
3786 3789/*
3787 if (type == 0x30) 3790 if (type == 0x30)
3788 { 3791 {
3789 if (m_shape.SculptEntry && m_shape.SculptTexture != UUID.Zero) 3792 if (m_shape.SculptEntry && m_shape.SculptTexture != UUID.Zero)
@@ -3791,7 +3794,7 @@ namespace OpenSim.Region.Framework.Scenes
3791 ParentGroup.Scene.AssetService.Get(m_shape.SculptTexture.ToString(), this, AssetReceived); 3794 ParentGroup.Scene.AssetService.Get(m_shape.SculptTexture.ToString(), this, AssetReceived);
3792 } 3795 }
3793 } 3796 }
3794 3797*/
3795 if (ParentGroup != null) 3798 if (ParentGroup != null)
3796 { 3799 {
3797 ParentGroup.HasGroupChanged = true; 3800 ParentGroup.HasGroupChanged = true;
@@ -4025,14 +4028,6 @@ namespace OpenSim.Region.Framework.Scenes
4025 if (!wasUsingPhysics) 4028 if (!wasUsingPhysics)
4026 { 4029 {
4027 DoPhysicsPropertyUpdate(UsePhysics, false); 4030 DoPhysicsPropertyUpdate(UsePhysics, false);
4028
4029 if (!ParentGroup.IsDeleted)
4030 {
4031 if (LocalId == ParentGroup.RootPart.LocalId)
4032 {
4033 ParentGroup.CheckSculptAndLoad();
4034 }
4035 }
4036 } 4031 }
4037 } 4032 }
4038 else 4033 else
@@ -4072,14 +4067,6 @@ namespace OpenSim.Region.Framework.Scenes
4072 pa.SetMaterial(Material); 4067 pa.SetMaterial(Material);
4073 DoPhysicsPropertyUpdate(UsePhysics, true); 4068 DoPhysicsPropertyUpdate(UsePhysics, true);
4074 4069
4075 if (!ParentGroup.IsDeleted)
4076 {
4077 if (LocalId == ParentGroup.RootPart.LocalId)
4078 {
4079 ParentGroup.CheckSculptAndLoad();
4080 }
4081 }
4082
4083 if ( 4070 if (
4084 ((AggregateScriptEvents & scriptEvents.collision) != 0) || 4071 ((AggregateScriptEvents & scriptEvents.collision) != 0) ||
4085 ((AggregateScriptEvents & scriptEvents.collision_end) != 0) || 4072 ((AggregateScriptEvents & scriptEvents.collision_end) != 0) ||
@@ -4104,14 +4091,6 @@ namespace OpenSim.Region.Framework.Scenes
4104 else // it already has a physical representation 4091 else // it already has a physical representation
4105 { 4092 {
4106 DoPhysicsPropertyUpdate(UsePhysics, false); // Update physical status. If it's phantom this will remove the prim 4093 DoPhysicsPropertyUpdate(UsePhysics, false); // Update physical status. If it's phantom this will remove the prim
4107
4108 if (!ParentGroup.IsDeleted)
4109 {
4110 if (LocalId == ParentGroup.RootPart.LocalId)
4111 {
4112 ParentGroup.CheckSculptAndLoad();
4113 }
4114 }
4115 } 4094 }
4116 } 4095 }
4117 4096
@@ -4341,6 +4320,7 @@ namespace OpenSim.Region.Framework.Scenes
4341 /// <remarks> 4320 /// <remarks>
4342 /// When the physics engine has finished with it, the sculpt data is discarded to save memory. 4321 /// When the physics engine has finished with it, the sculpt data is discarded to save memory.
4343 /// </remarks> 4322 /// </remarks>
4323/*
4344 public void CheckSculptAndLoad() 4324 public void CheckSculptAndLoad()
4345 { 4325 {
4346// m_log.DebugFormat("Processing CheckSculptAndLoad for {0} {1}", Name, LocalId); 4326// m_log.DebugFormat("Processing CheckSculptAndLoad for {0} {1}", Name, LocalId);
@@ -4366,7 +4346,7 @@ namespace OpenSim.Region.Framework.Scenes
4366 } 4346 }
4367 } 4347 }
4368 } 4348 }
4369 4349*/
4370 /// <summary> 4350 /// <summary>
4371 /// Update the texture entry for this part. 4351 /// Update the texture entry for this part.
4372 /// </summary> 4352 /// </summary>
@@ -4604,6 +4584,7 @@ namespace OpenSim.Region.Framework.Scenes
4604 } 4584 }
4605 4585
4606 Quaternion rot = Quaternion.Slerp(RotationOffset,APIDTarget,1.0f/(float)m_APIDIterations); 4586 Quaternion rot = Quaternion.Slerp(RotationOffset,APIDTarget,1.0f/(float)m_APIDIterations);
4587 rot.Normalize();
4607 UpdateRotation(rot); 4588 UpdateRotation(rot);
4608 4589
4609 m_APIDIterations--; 4590 m_APIDIterations--;
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs
index 821fd81..bdb0446 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs
@@ -92,6 +92,15 @@ namespace OpenSim.Region.Framework.Scenes
92 QueryScriptStates(); 92 QueryScriptStates();
93 } 93 }
94 } 94 }
95
96 public int Count
97 {
98 get
99 {
100 lock (m_items)
101 return m_items.Count;
102 }
103 }
95 104
96 /// <summary> 105 /// <summary>
97 /// Constructor 106 /// Constructor
diff --git a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs
index 28cd09f..e238d01 100644
--- a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs
+++ b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs
@@ -66,9 +66,9 @@ namespace OpenSim.Region.Framework.Scenes
66// /// </summary> 66// /// </summary>
67// private bool m_waitingForObjectAsset; 67// private bool m_waitingForObjectAsset;
68 68
69 public UuidGatherer(IAssetService assetCache) 69 public UuidGatherer(IAssetService assetService)
70 { 70 {
71 m_assetService = assetCache; 71 m_assetService = assetService;
72 } 72 }
73 73
74 /// <summary> 74 /// <summary>
diff --git a/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs b/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs
index d68aabc..68bcb4a 100644
--- a/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs
@@ -146,7 +146,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
146 sb.AppendFormat("Attachments for {0}\n", sp.Name); 146 sb.AppendFormat("Attachments for {0}\n", sp.Name);
147 147
148 ConsoleDisplayTable ct = new ConsoleDisplayTable() { Indent = 2 }; 148 ConsoleDisplayTable ct = new ConsoleDisplayTable() { Indent = 2 };
149 ct.Columns.Add(new ConsoleDisplayTableColumn("Attachment Name", 36)); 149 ct.Columns.Add(new ConsoleDisplayTableColumn("Attachment Name", 50));
150 ct.Columns.Add(new ConsoleDisplayTableColumn("Local ID", 10)); 150 ct.Columns.Add(new ConsoleDisplayTableColumn("Local ID", 10));
151 ct.Columns.Add(new ConsoleDisplayTableColumn("Item ID", 36)); 151 ct.Columns.Add(new ConsoleDisplayTableColumn("Item ID", 36));
152 ct.Columns.Add(new ConsoleDisplayTableColumn("Attach Point", 14)); 152 ct.Columns.Add(new ConsoleDisplayTableColumn("Attach Point", 14));
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs
index 10b83e6..1528330 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs
@@ -27,6 +27,7 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Linq;
30using System.Reflection; 31using System.Reflection;
31using log4net; 32using log4net;
32using Mono.Addins; 33using Mono.Addins;
@@ -36,6 +37,8 @@ using OpenMetaverse.StructuredData;
36using OpenSim.Framework; 37using OpenSim.Framework;
37using OpenSim.Region.Framework.Interfaces; 38using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Framework.Scenes; 39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Services.Interfaces;
41using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
39 42
40namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups 43namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
41{ 44{
@@ -45,6 +48,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46 49
47 private List<Scene> m_sceneList = new List<Scene>(); 50 private List<Scene> m_sceneList = new List<Scene>();
51 private IPresenceService m_presenceService;
48 52
49 private IMessageTransferModule m_msgTransferModule = null; 53 private IMessageTransferModule m_msgTransferModule = null;
50 54
@@ -54,6 +58,27 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
54 private bool m_groupMessagingEnabled = false; 58 private bool m_groupMessagingEnabled = false;
55 private bool m_debugEnabled = true; 59 private bool m_debugEnabled = true;
56 60
61 /// <summary>
62 /// If enabled, module only tries to send group IMs to online users by querying cached presence information.
63 /// </summary>
64 private bool m_messageOnlineAgentsOnly;
65
66 /// <summary>
67 /// Cache for online users.
68 /// </summary>
69 /// <remarks>
70 /// Group ID is key, presence information for online members is value.
71 /// Will only be non-null if m_messageOnlineAgentsOnly = true
72 /// We cache here so that group messages don't constantly have to re-request the online user list to avoid
73 /// attempted expensive sending of messages to offline users.
74 /// The tradeoff is that a user that comes online will not receive messages consistently from all other users
75 /// until caches have updated.
76 /// Therefore, we set the cache expiry to just 20 seconds.
77 /// </remarks>
78 private ExpiringCache<UUID, PresenceInfo[]> m_usersOnlineCache;
79
80 private int m_usersOnlineCacheExpirySeconds = 20;
81
57 #region IRegionModuleBase Members 82 #region IRegionModuleBase Members
58 83
59 public void Initialise(IConfigSource config) 84 public void Initialise(IConfigSource config)
@@ -83,10 +108,17 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
83 return; 108 return;
84 } 109 }
85 110
111 m_messageOnlineAgentsOnly = groupsConfig.GetBoolean("MessageOnlineUsersOnly", false);
112
113 if (m_messageOnlineAgentsOnly)
114 m_usersOnlineCache = new ExpiringCache<UUID, PresenceInfo[]>();
115
86 m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true); 116 m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true);
87 } 117 }
88 118
89 m_log.Info("[GROUPS-MESSAGING]: GroupsMessagingModule starting up"); 119 m_log.InfoFormat(
120 "[GROUPS-MESSAGING]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}",
121 m_messageOnlineAgentsOnly, m_debugEnabled);
90 } 122 }
91 123
92 public void AddRegion(Scene scene) 124 public void AddRegion(Scene scene)
@@ -126,6 +158,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
126 return; 158 return;
127 } 159 }
128 160
161 if (m_presenceService == null)
162 m_presenceService = scene.PresenceService;
129 163
130 m_sceneList.Add(scene); 164 m_sceneList.Add(scene);
131 165
@@ -207,12 +241,42 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
207 public void SendMessageToGroup(GridInstantMessage im, UUID groupID) 241 public void SendMessageToGroup(GridInstantMessage im, UUID groupID)
208 { 242 {
209 List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID), groupID); 243 List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID), groupID);
210 244 int groupMembersCount = groupMembers.Count;
211 if (m_debugEnabled) 245
212 m_log.DebugFormat( 246 if (m_messageOnlineAgentsOnly)
213 "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members", 247 {
214 groupID, groupMembers.Count); 248 string[] t1 = groupMembers.ConvertAll<string>(gmd => gmd.AgentID.ToString()).ToArray();
215 249
250 // We cache in order not to overwhlem the presence service on large grids with many groups. This does
251 // mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed.
252 // (assuming this is the same across all grid simulators).
253 PresenceInfo[] onlineAgents;
254 if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents))
255 {
256 onlineAgents = m_presenceService.GetAgents(t1);
257 m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds);
258 }
259
260 HashSet<string> onlineAgentsUuidSet = new HashSet<string>();
261 Array.ForEach<PresenceInfo>(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID));
262
263 groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList();
264
265 // if (m_debugEnabled)
266// m_log.DebugFormat(
267// "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members, {2} online",
268// groupID, groupMembersCount, groupMembers.Count());
269 }
270 else
271 {
272 if (m_debugEnabled)
273 m_log.DebugFormat(
274 "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members",
275 groupID, groupMembers.Count);
276 }
277
278 int requestStartTick = Environment.TickCount;
279
216 foreach (GroupMembersData member in groupMembers) 280 foreach (GroupMembersData member in groupMembers)
217 { 281 {
218 if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID)) 282 if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID))
@@ -254,6 +318,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
254 ProcessMessageFromGroupSession(msg); 318 ProcessMessageFromGroupSession(msg);
255 } 319 }
256 } 320 }
321
322 // Temporary for assessing how long it still takes to send messages to large online groups.
323 if (m_messageOnlineAgentsOnly)
324 m_log.DebugFormat(
325 "[GROUPS-MESSAGING]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms",
326 groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick);
257 } 327 }
258 328
259 #region SimGridEventHandlers 329 #region SimGridEventHandlers
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
index e669f4c..b9b4413 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
@@ -123,7 +123,36 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
123 public void AddRegion(Scene scene) 123 public void AddRegion(Scene scene)
124 { 124 {
125 if (m_groupsEnabled) 125 if (m_groupsEnabled)
126 {
126 scene.RegisterModuleInterface<IGroupsModule>(this); 127 scene.RegisterModuleInterface<IGroupsModule>(this);
128 scene.AddCommand(
129 "debug",
130 this,
131 "debug groups verbose",
132 "debug groups verbose <true|false>",
133 "This setting turns on very verbose groups debugging",
134 HandleDebugGroupsVerbose);
135 }
136 }
137
138 private void HandleDebugGroupsVerbose(object modules, string[] args)
139 {
140 if (args.Length < 4)
141 {
142 MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
143 return;
144 }
145
146 bool verbose = false;
147 if (!bool.TryParse(args[3], out verbose))
148 {
149 MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
150 return;
151 }
152
153 m_debugEnabled = verbose;
154
155 MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled);
127 } 156 }
128 157
129 public void RegionLoaded(Scene scene) 158 public void RegionLoaded(Scene scene)
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index 2a52e01..623ac8f 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -41,8 +41,6 @@ public class BSCharacter : BSPhysObject
41 41
42 // private bool _stopped; 42 // private bool _stopped;
43 private OMV.Vector3 _size; 43 private OMV.Vector3 _size;
44 private OMV.Vector3 _scale;
45 private PrimitiveBaseShape _pbs;
46 private bool _grabbed; 44 private bool _grabbed;
47 private bool _selected; 45 private bool _selected;
48 private OMV.Vector3 _position; 46 private OMV.Vector3 _position;
@@ -67,6 +65,10 @@ public class BSCharacter : BSPhysObject
67 private bool _kinematic; 65 private bool _kinematic;
68 private float _buoyancy; 66 private float _buoyancy;
69 67
68 // The friction and velocity of the avatar is modified depending on whether walking or not.
69 private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar
70 private float _currentFriction; // the friction currently being used (changed by setVelocity).
71
70 private OMV.Vector3 _PIDTarget; 72 private OMV.Vector3 _PIDTarget;
71 private bool _usePID; 73 private bool _usePID;
72 private float _PIDTau; 74 private float _PIDTau;
@@ -84,14 +86,18 @@ public class BSCharacter : BSPhysObject
84 _flying = isFlying; 86 _flying = isFlying;
85 _orientation = OMV.Quaternion.Identity; 87 _orientation = OMV.Quaternion.Identity;
86 _velocity = OMV.Vector3.Zero; 88 _velocity = OMV.Vector3.Zero;
89 _appliedVelocity = OMV.Vector3.Zero;
87 _buoyancy = ComputeBuoyancyFromFlying(isFlying); 90 _buoyancy = ComputeBuoyancyFromFlying(isFlying);
91 _currentFriction = PhysicsScene.Params.avatarStandingFriction;
92 _avatarDensity = PhysicsScene.Params.avatarDensity;
88 93
89 // The dimensions of the avatar capsule are kept in the scale. 94 // The dimensions of the avatar capsule are kept in the scale.
90 // Physics creates a unit capsule which is scaled by the physics engine. 95 // Physics creates a unit capsule which is scaled by the physics engine.
91 ComputeAvatarScale(_size); 96 ComputeAvatarScale(_size);
92 _avatarDensity = PhysicsScene.Params.avatarDensity; 97 // set _avatarVolume and _mass based on capsule size, _density and Scale
93 // set _avatarVolume and _mass based on capsule size, _density and _scale
94 ComputeAvatarVolumeAndMass(); 98 ComputeAvatarVolumeAndMass();
99 DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
100 LocalID, _size, Scale, _avatarDensity, _avatarVolume, MassRaw);
95 101
96 ShapeData shapeData = new ShapeData(); 102 ShapeData shapeData = new ShapeData();
97 shapeData.ID = LocalID; 103 shapeData.ID = LocalID;
@@ -99,28 +105,22 @@ public class BSCharacter : BSPhysObject
99 shapeData.Position = _position; 105 shapeData.Position = _position;
100 shapeData.Rotation = _orientation; 106 shapeData.Rotation = _orientation;
101 shapeData.Velocity = _velocity; 107 shapeData.Velocity = _velocity;
102 shapeData.Scale = _scale; 108 shapeData.Size = Scale;
109 shapeData.Scale = Scale;
103 shapeData.Mass = _mass; 110 shapeData.Mass = _mass;
104 shapeData.Buoyancy = _buoyancy; 111 shapeData.Buoyancy = _buoyancy;
105 shapeData.Static = ShapeData.numericFalse; 112 shapeData.Static = ShapeData.numericFalse;
106 shapeData.Friction = PhysicsScene.Params.avatarFriction; 113 shapeData.Friction = PhysicsScene.Params.avatarStandingFriction;
107 shapeData.Restitution = PhysicsScene.Params.avatarRestitution; 114 shapeData.Restitution = PhysicsScene.Params.avatarRestitution;
108 115
109 // do actual create at taint time 116 // do actual create at taint time
110 PhysicsScene.TaintedObject("BSCharacter.create", delegate() 117 PhysicsScene.TaintedObject("BSCharacter.create", delegate()
111 { 118 {
112 DetailLog("{0},BSCharacter.create,taint", LocalID); 119 DetailLog("{0},BSCharacter.create,taint", LocalID);
113 BulletSimAPI.CreateObject(PhysicsScene.WorldID, shapeData); 120 // New body and shape into BSBody and BSShape
114 121 PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this, shapeData, null, null, null);
115 // Set the buoyancy for flying. This will be refactored when all the settings happen in C#.
116 // If not set at creation, the avatar will stop flying when created after crossing a region boundry.
117 BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy);
118 122
119 BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(PhysicsScene.World.ptr, LocalID)); 123 SetPhysicalProperties();
120
121 // This works here because CreateObject has already put the character into the physical world.
122 BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr,
123 (uint)CollisionFilterGroups.AvatarFilter, (uint)CollisionFilterGroups.AvatarMask);
124 }); 124 });
125 return; 125 return;
126 } 126 }
@@ -131,53 +131,85 @@ public class BSCharacter : BSPhysObject
131 DetailLog("{0},BSCharacter.Destroy", LocalID); 131 DetailLog("{0},BSCharacter.Destroy", LocalID);
132 PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() 132 PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
133 { 133 {
134 BulletSimAPI.DestroyObject(PhysicsScene.WorldID, LocalID); 134 PhysicsScene.Shapes.DereferenceBody(BSBody, true, null);
135 PhysicsScene.Shapes.DereferenceShape(BSShape, true, null);
135 }); 136 });
136 } 137 }
137 138
139 private void SetPhysicalProperties()
140 {
141 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, BSBody.ptr);
142
143 ZeroMotion();
144 ForcePosition = _position;
145 // Set the velocity and compute the proper friction
146 ForceVelocity = _velocity;
147 BulletSimAPI.SetRestitution2(BSBody.ptr, PhysicsScene.Params.avatarRestitution);
148 BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale);
149 BulletSimAPI.SetContactProcessingThreshold2(BSBody.ptr, PhysicsScene.Params.contactProcessingThreshold);
150 if (PhysicsScene.Params.ccdMotionThreshold > 0f)
151 {
152 BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
153 BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
154 }
155
156 OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw);
157 BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia);
158
159 BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT);
160
161 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, BSBody.ptr);
162
163 BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
164 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, BSBody.ptr);
165
166 // Do this after the object has been added to the world
167 BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr,
168 (uint)CollisionFilterGroups.AvatarFilter,
169 (uint)CollisionFilterGroups.AvatarMask);
170 }
171
138 public override void RequestPhysicsterseUpdate() 172 public override void RequestPhysicsterseUpdate()
139 { 173 {
140 base.RequestPhysicsterseUpdate(); 174 base.RequestPhysicsterseUpdate();
141 } 175 }
142 // No one calls this method so I don't know what it could possibly mean 176 // No one calls this method so I don't know what it could possibly mean
143 public override bool Stopped { 177 public override bool Stopped { get { return false; } }
144 get { return false; }
145 }
146 public override OMV.Vector3 Size { 178 public override OMV.Vector3 Size {
147 get 179 get
148 { 180 {
149 // Avatar capsule size is kept in the scale parameter. 181 // Avatar capsule size is kept in the scale parameter.
150 return new OMV.Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z); 182 return _size;
151 } 183 }
152 184
153 set { 185 set {
154 // When an avatar's size is set, only the height is changed 186 // When an avatar's size is set, only the height is changed.
155 // and that really only depends on the radius.
156 _size = value; 187 _size = value;
157 ComputeAvatarScale(_size); 188 ComputeAvatarScale(_size);
158
159 // TODO: something has to be done with the avatar's vertical position
160
161 ComputeAvatarVolumeAndMass(); 189 ComputeAvatarVolumeAndMass();
190 DetailLog("{0},BSCharacter.setSize,call,scale={1},density={2},volume={3},mass={4}",
191 LocalID, Scale, _avatarDensity, _avatarVolume, MassRaw);
162 192
163 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() 193 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
164 { 194 {
165 BulletSimAPI.SetObjectScaleMass(PhysicsScene.WorldID, LocalID, _scale, _mass, true); 195 BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale);
196 OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw);
197 BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia);
166 }); 198 });
167 199
168 } 200 }
169 } 201 }
170 public override PrimitiveBaseShape Shape { 202 public override OMV.Vector3 Scale { get; set; }
171 set { _pbs = value; 203 public override PrimitiveBaseShape Shape
172 } 204 {
205 set { BaseShape = value; }
173 } 206 }
207
174 public override bool Grabbed { 208 public override bool Grabbed {
175 set { _grabbed = value; 209 set { _grabbed = value; }
176 }
177 } 210 }
178 public override bool Selected { 211 public override bool Selected {
179 set { _selected = value; 212 set { _selected = value; }
180 }
181 } 213 }
182 public override void CrossingFailure() { return; } 214 public override void CrossingFailure() { return; }
183 public override void link(PhysicsActor obj) { return; } 215 public override void link(PhysicsActor obj) { return; }
@@ -204,7 +236,7 @@ public class BSCharacter : BSPhysObject
204 236
205 public override OMV.Vector3 Position { 237 public override OMV.Vector3 Position {
206 get { 238 get {
207 // _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID); 239 // _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
208 return _position; 240 return _position;
209 } 241 }
210 set { 242 set {
@@ -214,7 +246,7 @@ public class BSCharacter : BSPhysObject
214 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() 246 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
215 { 247 {
216 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 248 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
217 BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); 249 BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
218 }); 250 });
219 } 251 }
220 } 252 }
@@ -263,7 +295,7 @@ public class BSCharacter : BSPhysObject
263 // A version of the sanity check that also makes sure a new position value is 295 // A version of the sanity check that also makes sure a new position value is
264 // pushed back to the physics engine. This routine would be used by anyone 296 // pushed back to the physics engine. This routine would be used by anyone
265 // who is not already pushing the value. 297 // who is not already pushing the value.
266 private bool PositionSanityCheck2(bool atTaintTime) 298 private bool PositionSanityCheck(bool inTaintTime)
267 { 299 {
268 bool ret = false; 300 bool ret = false;
269 if (PositionSanityCheck()) 301 if (PositionSanityCheck())
@@ -273,9 +305,9 @@ public class BSCharacter : BSPhysObject
273 BSScene.TaintCallback sanityOperation = delegate() 305 BSScene.TaintCallback sanityOperation = delegate()
274 { 306 {
275 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); 307 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
276 BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); 308 BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
277 }; 309 };
278 if (atTaintTime) 310 if (inTaintTime)
279 sanityOperation(); 311 sanityOperation();
280 else 312 else
281 PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", sanityOperation); 313 PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", sanityOperation);
@@ -284,11 +316,7 @@ public class BSCharacter : BSPhysObject
284 return ret; 316 return ret;
285 } 317 }
286 318
287 public override float Mass { 319 public override float Mass { get { return _mass; } }
288 get {
289 return _mass;
290 }
291 }
292 320
293 // used when we only want this prim's mass and not the linkset thing 321 // used when we only want this prim's mass and not the linkset thing
294 public override float MassRaw { get {return _mass; } } 322 public override float MassRaw { get {return _mass; } }
@@ -301,15 +329,13 @@ public class BSCharacter : BSPhysObject
301 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() 329 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
302 { 330 {
303 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); 331 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
304 BulletSimAPI.SetObjectForce(PhysicsScene.WorldID, LocalID, _force); 332 BulletSimAPI.SetObjectForce2(BSBody.ptr, _force);
305 }); 333 });
306 } 334 }
307 } 335 }
308 336
309 public override int VehicleType { 337 // Avatars don't do vehicles
310 get { return 0; } 338 public override int VehicleType { get { return 0; } set { return; } }
311 set { return; }
312 }
313 public override void VehicleFloatParam(int param, float value) { } 339 public override void VehicleFloatParam(int param, float value) { }
314 public override void VehicleVectorParam(int param, OMV.Vector3 value) {} 340 public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
315 public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { } 341 public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
@@ -328,10 +354,39 @@ public class BSCharacter : BSPhysObject
328 PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() 354 PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
329 { 355 {
330 DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity); 356 DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
331 BulletSimAPI.SetObjectVelocity(PhysicsScene.WorldID, LocalID, _velocity); 357 ForceVelocity = _velocity;
332 }); 358 });
333 } 359 }
334 } 360 }
361 public override OMV.Vector3 ForceVelocity {
362 get { return _velocity; }
363 set {
364 // Depending on whether the avatar is moving or not, change the friction
365 // to keep the avatar from slipping around
366 if (_velocity.Length() == 0)
367 {
368 if (_currentFriction != PhysicsScene.Params.avatarStandingFriction)
369 {
370 _currentFriction = PhysicsScene.Params.avatarStandingFriction;
371 BulletSimAPI.SetFriction2(BSBody.ptr, _currentFriction);
372 }
373 }
374 else
375 {
376 if (_currentFriction != PhysicsScene.Params.avatarFriction)
377 {
378 _currentFriction = PhysicsScene.Params.avatarFriction;
379 BulletSimAPI.SetFriction2(BSBody.ptr, _currentFriction);
380 }
381 }
382 _velocity = value;
383 // Remember the set velocity so we can suppress the reduction by friction, ...
384 _appliedVelocity = value;
385
386 BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity);
387 BulletSimAPI.Activate2(BSBody.ptr, true);
388 }
389 }
335 public override OMV.Vector3 Torque { 390 public override OMV.Vector3 Torque {
336 get { return _torque; } 391 get { return _torque; }
337 set { _torque = value; 392 set { _torque = value;
@@ -353,8 +408,8 @@ public class BSCharacter : BSPhysObject
353 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); 408 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
354 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() 409 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
355 { 410 {
356 // _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID); 411 // _position = BulletSimAPI.GetPosition2(BSBody.ptr);
357 BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); 412 BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
358 }); 413 });
359 } 414 }
360 } 415 }
@@ -382,12 +437,18 @@ public class BSCharacter : BSPhysObject
382 set { _isPhysical = value; 437 set { _isPhysical = value;
383 } 438 }
384 } 439 }
440 public override bool IsSolid {
441 get { return true; }
442 }
443 public override bool IsStatic {
444 get { return false; }
445 }
385 public override bool Flying { 446 public override bool Flying {
386 get { return _flying; } 447 get { return _flying; }
387 set { 448 set {
388 _flying = value; 449 _flying = value;
389 // simulate flying by changing the effect of gravity 450 // simulate flying by changing the effect of gravity
390 this.Buoyancy = ComputeBuoyancyFromFlying(_flying); 451 Buoyancy = ComputeBuoyancyFromFlying(_flying);
391 } 452 }
392 } 453 }
393 // Flying is implimented by changing the avatar's buoyancy. 454 // Flying is implimented by changing the avatar's buoyancy.
@@ -432,6 +493,10 @@ public class BSCharacter : BSPhysObject
432 get { return _rotationalVelocity; } 493 get { return _rotationalVelocity; }
433 set { _rotationalVelocity = value; } 494 set { _rotationalVelocity = value; }
434 } 495 }
496 public override OMV.Vector3 ForceRotationalVelocity {
497 get { return _rotationalVelocity; }
498 set { _rotationalVelocity = value; }
499 }
435 public override bool Kinematic { 500 public override bool Kinematic {
436 get { return _kinematic; } 501 get { return _kinematic; }
437 set { _kinematic = value; } 502 set { _kinematic = value; }
@@ -443,10 +508,19 @@ public class BSCharacter : BSPhysObject
443 PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate() 508 PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
444 { 509 {
445 DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 510 DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
446 BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy); 511 ForceBuoyancy = _buoyancy;
447 }); 512 });
448 } 513 }
449 } 514 }
515 public override float ForceBuoyancy {
516 get { return _buoyancy; }
517 set { _buoyancy = value;
518 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
519 // Buoyancy is faked by changing the gravity applied to the object
520 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
521 BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav));
522 }
523 }
450 524
451 // Used for MoveTo 525 // Used for MoveTo
452 public override OMV.Vector3 PIDTarget { 526 public override OMV.Vector3 PIDTarget {
@@ -507,27 +581,32 @@ public class BSCharacter : BSPhysObject
507 581
508 private void ComputeAvatarScale(OMV.Vector3 size) 582 private void ComputeAvatarScale(OMV.Vector3 size)
509 { 583 {
510 _scale.X = PhysicsScene.Params.avatarCapsuleRadius; 584 // The 'size' given by the simulator is the mid-point of the avatar
511 _scale.Y = PhysicsScene.Params.avatarCapsuleRadius; 585 // and X and Y are unspecified.
586
587 OMV.Vector3 newScale = OMV.Vector3.Zero;
588 newScale.X = PhysicsScene.Params.avatarCapsuleRadius;
589 newScale.Y = PhysicsScene.Params.avatarCapsuleRadius;
512 590
513 // The 1.15 came from ODE but it seems to cause the avatar to float off the ground 591 // From the total height, remote the capsule half spheres that are at each end
514 // _scale.Z = (_size.Z * 1.15f) - (_scale.X + _scale.Y); 592 newScale.Z = (size.Z * 2f) - Math.Min(newScale.X, newScale.Y);
515 _scale.Z = (_size.Z) - (_scale.X + _scale.Y); 593 // newScale.Z = (size.Z * 2f);
594 Scale = newScale;
516 } 595 }
517 596
518 // set _avatarVolume and _mass based on capsule size, _density and _scale 597 // set _avatarVolume and _mass based on capsule size, _density and Scale
519 private void ComputeAvatarVolumeAndMass() 598 private void ComputeAvatarVolumeAndMass()
520 { 599 {
521 _avatarVolume = (float)( 600 _avatarVolume = (float)(
522 Math.PI 601 Math.PI
523 * _scale.X 602 * Scale.X
524 * _scale.Y // the area of capsule cylinder 603 * Scale.Y // the area of capsule cylinder
525 * _scale.Z // times height of capsule cylinder 604 * Scale.Z // times height of capsule cylinder
526 + 1.33333333f 605 + 1.33333333f
527 * Math.PI 606 * Math.PI
528 * _scale.X 607 * Scale.X
529 * Math.Min(_scale.X, _scale.Y) 608 * Math.Min(Scale.X, Scale.Y)
530 * _scale.Y // plus the volume of the capsule end caps 609 * Scale.Y // plus the volume of the capsule end caps
531 ); 610 );
532 _mass = _avatarDensity * _avatarVolume; 611 _mass = _avatarDensity * _avatarVolume;
533 } 612 }
@@ -542,7 +621,23 @@ public class BSCharacter : BSPhysObject
542 _acceleration = entprop.Acceleration; 621 _acceleration = entprop.Acceleration;
543 _rotationalVelocity = entprop.RotationalVelocity; 622 _rotationalVelocity = entprop.RotationalVelocity;
544 // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. 623 // Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
545 PositionSanityCheck2(true); 624 PositionSanityCheck(true);
625
626 // remember the current and last set values
627 LastEntityProperties = CurrentEntityProperties;
628 CurrentEntityProperties = entprop;
629
630 if (entprop.Velocity != LastEntityProperties.Velocity)
631 {
632 // Changes in the velocity are suppressed in avatars.
633 // That's just the way they are defined.
634 OMV.Vector3 avVel = new OMV.Vector3(_appliedVelocity.X, _appliedVelocity.Y, entprop.Velocity.Z);
635 _velocity = avVel;
636 BulletSimAPI.SetLinearVelocity2(BSBody.ptr, avVel);
637 }
638
639 // Tell the linkset about this
640 Linkset.UpdateProperties(this);
546 641
547 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. 642 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
548 // base.RequestPhysicsterseUpdate(); 643 // base.RequestPhysicsterseUpdate();
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
index 63a4127..a20be3a 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
@@ -49,9 +49,16 @@ public abstract class BSConstraint : IDisposable
49 if (m_enabled) 49 if (m_enabled)
50 { 50 {
51 m_enabled = false; 51 m_enabled = false;
52 bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr); 52 if (m_constraint.ptr != IntPtr.Zero)
53 m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success); 53 {
54 m_constraint.ptr = System.IntPtr.Zero; 54 bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
55 m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}",
56 BSScene.DetailLogZero,
57 m_body1.ID, m_body1.ptr.ToString("X"),
58 m_body2.ID, m_body2.ptr.ToString("X"),
59 success);
60 m_constraint.ptr = System.IntPtr.Zero;
61 }
55 } 62 }
56 } 63 }
57 64
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
index 4ba2f62..56342b8 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -462,7 +462,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
462 return; 462 return;
463 463
464 // Set the prim's inertia to zero. The vehicle code handles that and this 464 // Set the prim's inertia to zero. The vehicle code handles that and this
465 // removes the torque action introduced by Bullet. 465 // removes the motion and torque actions introduced by Bullet.
466 Vector3 inertia = Vector3.Zero; 466 Vector3 inertia = Vector3.Zero;
467 BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia); 467 BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia);
468 BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr); 468 BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr);
@@ -481,7 +481,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
481 m_lastPositionVector = Prim.ForcePosition; 481 m_lastPositionVector = Prim.ForcePosition;
482 482
483 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", 483 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
484 Prim.LocalID, Prim.Position, Prim.Force, Prim.Velocity, Prim.RotationalVelocity); 484 Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity);
485 }// end Step 485 }// end Step
486 486
487 // Apply the effect of the linear motor. 487 // Apply the effect of the linear motor.
@@ -540,7 +540,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
540 // add Gravity and Buoyancy 540 // add Gravity and Buoyancy
541 // There is some gravity, make a gravity force vector that is applied after object velocity. 541 // There is some gravity, make a gravity force vector that is applied after object velocity.
542 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; 542 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
543 Vector3 grav = Prim.PhysicsScene.DefaultGravity * (Prim.Mass * (1f - m_VehicleBuoyancy)); 543 Vector3 grav = Prim.PhysicsScene.DefaultGravity * (Prim.Linkset.LinksetMass * (1f - m_VehicleBuoyancy));
544 544
545 /* 545 /*
546 * RA: Not sure why one would do this 546 * RA: Not sure why one would do this
@@ -678,10 +678,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
678 m_newVelocity.Z = 0; 678 m_newVelocity.Z = 0;
679 679
680 // Apply velocity 680 // Apply velocity
681 Prim.Velocity = m_newVelocity; 681 Prim.ForceVelocity = m_newVelocity;
682 // apply gravity force 682 // apply gravity force
683 // Why is this set here? The physics engine already does gravity. 683 // Why is this set here? The physics engine already does gravity.
684 // m_prim.AddForce(grav, false); 684 Prim.AddForce(grav, false, true);
685 685
686 // Apply friction 686 // Apply friction
687 Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep)); 687 Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep));
@@ -704,7 +704,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
704 // m_lastAngularVelocity // what was last applied to body 704 // m_lastAngularVelocity // what was last applied to body
705 705
706 // Get what the body is doing, this includes 'external' influences 706 // Get what the body is doing, this includes 'external' influences
707 Vector3 angularVelocity = Prim.RotationalVelocity; 707 Vector3 angularVelocity = Prim.ForceRotationalVelocity;
708 708
709 if (m_angularMotorApply > 0) 709 if (m_angularMotorApply > 0)
710 { 710 {
@@ -810,7 +810,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
810 m_lastAngularVelocity -= m_lastAngularVelocity * decayamount; 810 m_lastAngularVelocity -= m_lastAngularVelocity * decayamount;
811 811
812 // Apply to the body 812 // Apply to the body
813 Prim.RotationalVelocity = m_lastAngularVelocity; 813 Prim.ForceRotationalVelocity = m_lastAngularVelocity;
814 814
815 VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", Prim.LocalID, decayamount, m_lastAngularVelocity); 815 VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", Prim.LocalID, decayamount, m_lastAngularVelocity);
816 } //end MoveAngular 816 } //end MoveAngular
@@ -862,7 +862,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
862 private void VDetailLog(string msg, params Object[] args) 862 private void VDetailLog(string msg, params Object[] args)
863 { 863 {
864 if (Prim.PhysicsScene.VehicleLoggingEnabled) 864 if (Prim.PhysicsScene.VehicleLoggingEnabled)
865 Prim.PhysicsScene.PhysicsLogging.Write(msg, args); 865 Prim.PhysicsScene.DetailLog(msg, args);
866 } 866 }
867 } 867 }
868} 868}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
index 3e82642..43b1262 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
@@ -52,8 +52,8 @@ public class BSLinkset
52 // the physical 'taint' children separately. 52 // the physical 'taint' children separately.
53 // After taint processing and before the simulation step, these 53 // After taint processing and before the simulation step, these
54 // two lists must be the same. 54 // two lists must be the same.
55 private List<BSPhysObject> m_children; 55 private HashSet<BSPhysObject> m_children;
56 private List<BSPhysObject> m_taintChildren; 56 private HashSet<BSPhysObject> m_taintChildren;
57 57
58 // We lock the diddling of linkset classes to prevent any badness. 58 // We lock the diddling of linkset classes to prevent any badness.
59 // This locks the modification of the instances of this class. Changes 59 // This locks the modification of the instances of this class. Changes
@@ -90,8 +90,8 @@ public class BSLinkset
90 m_nextLinksetID = 1; 90 m_nextLinksetID = 1;
91 PhysicsScene = scene; 91 PhysicsScene = scene;
92 LinksetRoot = parent; 92 LinksetRoot = parent;
93 m_children = new List<BSPhysObject>(); 93 m_children = new HashSet<BSPhysObject>();
94 m_taintChildren = new List<BSPhysObject>(); 94 m_taintChildren = new HashSet<BSPhysObject>();
95 m_mass = parent.MassRaw; 95 m_mass = parent.MassRaw;
96 } 96 }
97 97
@@ -160,6 +160,28 @@ public class BSLinkset
160 return ret; 160 return ret;
161 } 161 }
162 162
163 // When physical properties are changed the linkset needs to recalculate
164 // its internal properties.
165 // May be called at runtime or taint-time (just pass the appropriate flag).
166 public void Refresh(BSPhysObject requestor, bool inTaintTime)
167 {
168 // If there are no children, not physical or not root, I am not the one that recomputes the constraints
169 // (For the moment, static linksets do create constraints so remove the test for physical.)
170 if (!HasAnyChildren || /*!requestor.IsPhysical ||*/ !IsRoot(requestor))
171 return;
172
173 BSScene.TaintCallback refreshOperation = delegate()
174 {
175 RecomputeLinksetConstraintVariables();
176 DetailLog("{0},BSLinkset.Refresh,complete,rBody={1}",
177 LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"));
178 };
179 if (inTaintTime)
180 refreshOperation();
181 else
182 PhysicsScene.TaintedObject("BSLinkSet.Refresh", refreshOperation);
183 }
184
163 // The object is going dynamic (physical). Do any setup necessary 185 // The object is going dynamic (physical). Do any setup necessary
164 // for a dynamic linkset. 186 // for a dynamic linkset.
165 // Only the state of the passed object can be modified. The rest of the linkset 187 // Only the state of the passed object can be modified. The rest of the linkset
@@ -182,22 +204,19 @@ public class BSLinkset
182 return false; 204 return false;
183 } 205 }
184 206
185 // When physical properties are changed the linkset needs to recalculate 207 // If the software is handling the movement of all the objects in a linkset
186 // its internal properties. 208 // (like if one doesn't use constraints for static linksets), this is called
187 // Called at runtime. 209 // when an update for the root of the linkset is received.
188 public void Refresh(BSPhysObject requestor) 210 // Called at taint-time!!
211 public void UpdateProperties(BSPhysObject physObject)
189 { 212 {
190 // If there are no children, there can't be any constraints to recompute 213 // The root local properties have been updated. Apply to the children if appropriate.
191 if (!HasAnyChildren) 214 if (IsRoot(physObject) && HasAnyChildren)
192 return;
193
194 // Only the root does the recomputation
195 if (IsRoot(requestor))
196 { 215 {
197 PhysicsScene.TaintedObject("BSLinkSet.Refresh", delegate() 216 if (!physObject.IsPhysical)
198 { 217 {
199 RecomputeLinksetConstraintVariables(); 218 // TODO: implement software linkset update for static object linksets
200 }); 219 }
201 } 220 }
202 } 221 }
203 222
@@ -215,13 +234,10 @@ public class BSLinkset
215 if (IsRoot(child)) 234 if (IsRoot(child))
216 { 235 {
217 // If the one with the dependency is root, must undo all children 236 // If the one with the dependency is root, must undo all children
218 DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},numChild={2}", 237 DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
219 child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); 238 child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"));
220 foreach (BSPhysObject bpo in m_taintChildren) 239
221 { 240 ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
222 PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody);
223 ret = true;
224 }
225 } 241 }
226 else 242 else
227 { 243 {
@@ -229,20 +245,16 @@ public class BSLinkset
229 child.LocalID, 245 child.LocalID,
230 LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), 246 LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
231 child.LocalID, child.BSBody.ptr.ToString("X")); 247 child.LocalID, child.BSBody.ptr.ToString("X"));
232 // Remove the dependency on the body of this one 248 // ret = PhysicallyUnlinkAChildFromRoot(LinksetRoot, child);
233 if (m_taintChildren.Contains(child)) 249 // Despite the function name, this removes any link to the specified object.
234 { 250 ret = PhysicallyUnlinkAllChildrenFromRoot(child);
235 PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody);
236 ret = true;
237 }
238 } 251 }
239 } 252 }
240 return ret; 253 return ret;
241 } 254 }
242 255
243 // Routine used when rebuilding the body of the root of the linkset 256 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
244 // This is called after RemoveAllLinksToRoot() to restore all the constraints. 257 // this routine will restore the removed constraints.
245 // This is called when the root body has been changed.
246 // Called at taint-time!! 258 // Called at taint-time!!
247 public void RestoreBodyDependencies(BSPrim child) 259 public void RestoreBodyDependencies(BSPrim child)
248 { 260 {
@@ -254,7 +266,7 @@ public class BSLinkset
254 child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); 266 child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count);
255 foreach (BSPhysObject bpo in m_taintChildren) 267 foreach (BSPhysObject bpo in m_taintChildren)
256 { 268 {
257 PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody); 269 PhysicallyLinkAChildToRoot(LinksetRoot, bpo);
258 } 270 }
259 } 271 }
260 else 272 else
@@ -263,7 +275,7 @@ public class BSLinkset
263 LinksetRoot.LocalID, 275 LinksetRoot.LocalID,
264 LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), 276 LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
265 child.LocalID, child.BSBody.ptr.ToString("X")); 277 child.LocalID, child.BSBody.ptr.ToString("X"));
266 PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody); 278 PhysicallyLinkAChildToRoot(LinksetRoot, child);
267 } 279 }
268 } 280 }
269 } 281 }
@@ -330,22 +342,22 @@ public class BSLinkset
330 { 342 {
331 m_children.Add(child); 343 m_children.Add(child);
332 344
333 BSPhysObject rootx = LinksetRoot; // capture the root and body as of now 345 BSPhysObject rootx = LinksetRoot; // capture the root as of now
334 BSPhysObject childx = child; 346 BSPhysObject childx = child;
335 347
336 DetailLog("{0},AddChildToLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", 348 DetailLog("{0},AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
337 rootx.LocalID,
338 rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
339 childx.LocalID, childx.BSBody.ptr.ToString("X"));
340 349
341 PhysicsScene.TaintedObject("AddChildToLinkset", delegate() 350 PhysicsScene.TaintedObject("AddChildToLinkset", delegate()
342 { 351 {
343 DetailLog("{0},AddChildToLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID); 352 DetailLog("{0},AddChildToLinkset,taint,rID={1},rBody={2},cID={3},cBody={4}",
344 // build the physical binding between me and the child 353 rootx.LocalID,
345 m_taintChildren.Add(childx); 354 rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
346 355 childx.LocalID, childx.BSBody.ptr.ToString("X"));
347 // Since this is taint-time, the body and shape could have changed for the child 356 // Since this is taint-time, the body and shape could have changed for the child
348 PhysicallyLinkAChildToRoot(rootx, rootx.BSBody, childx, childx.BSBody); 357 rootx.ForcePosition = rootx.Position; // DEBUG
358 childx.ForcePosition = childx.Position; // DEBUG
359 PhysicallyLinkAChildToRoot(rootx, childx);
360 m_taintChildren.Add(child);
349 }); 361 });
350 } 362 }
351 return; 363 return;
@@ -378,10 +390,8 @@ public class BSLinkset
378 390
379 PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() 391 PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate()
380 { 392 {
381 if (m_taintChildren.Contains(childx)) 393 m_taintChildren.Remove(child);
382 m_taintChildren.Remove(childx); 394 PhysicallyUnlinkAChildFromRoot(rootx, childx);
383
384 PhysicallyUnlinkAChildFromRoot(rootx, rootx.BSBody, childx, childx.BSBody);
385 RecomputeLinksetConstraintVariables(); 395 RecomputeLinksetConstraintVariables();
386 }); 396 });
387 397
@@ -396,8 +406,7 @@ public class BSLinkset
396 406
397 // Create a constraint between me (root of linkset) and the passed prim (the child). 407 // Create a constraint between me (root of linkset) and the passed prim (the child).
398 // Called at taint time! 408 // Called at taint time!
399 private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BulletBody rootBody, 409 private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
400 BSPhysObject childPrim, BulletBody childBody)
401 { 410 {
402 // Zero motion for children so they don't interpolate 411 // Zero motion for children so they don't interpolate
403 childPrim.ZeroMotion(); 412 childPrim.ZeroMotion();
@@ -409,33 +418,17 @@ public class BSLinkset
409 // real world coordinate of midpoint between the two objects 418 // real world coordinate of midpoint between the two objects
410 OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); 419 OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
411 420
412 DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", 421 DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
413 rootPrim.LocalID, 422 rootPrim.LocalID,
414 rootPrim.LocalID, rootBody.ptr.ToString("X"), 423 rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"),
415 childPrim.LocalID, childBody.ptr.ToString("X"), 424 childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"),
416 rootPrim.Position, childPrim.Position, midPoint); 425 rootPrim.Position, childPrim.Position, midPoint);
417 426
418 // create a constraint that allows no freedom of movement between the two objects 427 // create a constraint that allows no freedom of movement between the two objects
419 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 428 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
420 429
421 // There is great subtlty in these paramters. Notice the check for a ptr of zero.
422 // We pass the BulletBody structure into the taint in order to capture the pointer
423 // of the body at the time of constraint creation. This doesn't work for the very first
424 // construction because there is no body yet. The body
425 // is constructed later at taint time. Thus we use the body address at time of the
426 // taint creation but, if it is zero, use what's in the prim at the moment.
427 // There is a possible race condition since shape can change without a taint call
428 // (like changing to a mesh that is already constructed). The fix for that would be
429 // to only change BSShape at taint time thus syncronizing these operations at
430 // the cost of efficiency and lag.
431 BS6DofConstraint constrain = new BS6DofConstraint( 430 BS6DofConstraint constrain = new BS6DofConstraint(
432 PhysicsScene.World, 431 PhysicsScene.World, rootPrim.BSBody, childPrim.BSBody, midPoint, true, true );
433 rootBody.ptr == IntPtr.Zero ? rootPrim.BSBody : rootBody,
434 childBody.ptr == IntPtr.Zero ? childPrim.BSBody : childBody,
435 midPoint,
436 true,
437 true
438 );
439 432
440 /* NOTE: below is an attempt to build constraint with full frame computation, etc. 433 /* NOTE: below is an attempt to build constraint with full frame computation, etc.
441 * Using the midpoint is easier since it lets the Bullet code manipulate the transforms 434 * Using the midpoint is easier since it lets the Bullet code manipulate the transforms
@@ -452,7 +445,7 @@ public class BSLinkset
452 445
453 // create a constraint that allows no freedom of movement between the two objects 446 // create a constraint that allows no freedom of movement between the two objects
454 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 447 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
455 DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID); 448 DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
456 BS6DofConstraint constrain = new BS6DofConstraint( 449 BS6DofConstraint constrain = new BS6DofConstraint(
457 PhysicsScene.World, rootPrim.Body, childPrim.Body, 450 PhysicsScene.World, rootPrim.Body, childPrim.Body,
458 OMV.Vector3.Zero, 451 OMV.Vector3.Zero,
@@ -486,39 +479,44 @@ public class BSLinkset
486 { 479 {
487 constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); 480 constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations);
488 } 481 }
489
490 RecomputeLinksetConstraintVariables();
491 } 482 }
492 483
493 // Remove linkage between myself and a particular child 484 // Remove linkage between myself and a particular child
494 // The root and child bodies are passed in because we need to remove the constraint between 485 // The root and child bodies are passed in because we need to remove the constraint between
495 // the bodies that were at unlink time. 486 // the bodies that were at unlink time.
496 // Called at taint time! 487 // Called at taint time!
497 private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BulletBody rootBody, 488 private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
498 BSPhysObject childPrim, BulletBody childBody)
499 { 489 {
500 DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", 490 bool ret = false;
491 DetailLog("{0},BSLinkset.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
501 rootPrim.LocalID, 492 rootPrim.LocalID,
502 rootPrim.LocalID, rootBody.ptr.ToString("X"), 493 rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"),
503 childPrim.LocalID, childBody.ptr.ToString("X")); 494 childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"));
504 495
505 // Find the constraint for this link and get rid of it from the overall collection and from my list 496 // Find the constraint for this link and get rid of it from the overall collection and from my list
506 PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootBody, childBody); 497 if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody))
498 {
499 // Make the child refresh its location
500 BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr);
501 ret = true;
502 }
507 503
508 // Make the child refresh its location 504 return ret;
509 BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr);
510 } 505 }
511 506
512 /*
513 // Remove linkage between myself and any possible children I might have. 507 // Remove linkage between myself and any possible children I might have.
514 // Called at taint time! 508 // Called at taint time!
515 private void PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) 509 private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
516 { 510 {
517 DetailLog("{0},PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); 511 DetailLog("{0},BSLinkset.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
512 bool ret = false;
518 513
519 PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody); 514 if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody))
515 {
516 ret = true;
517 }
518 return ret;
520 } 519 }
521 */
522 520
523 // Call each of the constraints that make up this linkset and recompute the 521 // Call each of the constraints that make up this linkset and recompute the
524 // various transforms and variables. Used when objects are added or removed 522 // various transforms and variables. Used when objects are added or removed
@@ -550,11 +548,17 @@ public class BSLinkset
550 { 548 {
551 // If this is a multiple object linkset, set everybody's center of mass to the set's center of mass 549 // If this is a multiple object linkset, set everybody's center of mass to the set's center of mass
552 OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass(); 550 OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
553 BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr, centerOfMass, OMV.Quaternion.Identity); 551 BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr,
552 centerOfMass, OMV.Quaternion.Identity);
553 DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,setCenterOfMass,COM={1},rBody={2}",
554 LinksetRoot.LocalID, centerOfMass, LinksetRoot.BSBody.ptr.ToString("X"));
554 foreach (BSPhysObject child in m_taintChildren) 555 foreach (BSPhysObject child in m_taintChildren)
555 { 556 {
556 BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr, centerOfMass, OMV.Quaternion.Identity); 557 BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr,
558 centerOfMass, OMV.Quaternion.Identity);
557 } 559 }
560
561 // BulletSimAPI.DumpAllInfo2(PhysicsScene.World.ptr); // DEBUG DEBUG DEBUG
558 } 562 }
559 return; 563 return;
560 } 564 }
@@ -563,7 +567,8 @@ public class BSLinkset
563 // Invoke the detailed logger and output something if it's enabled. 567 // Invoke the detailed logger and output something if it's enabled.
564 private void DetailLog(string msg, params Object[] args) 568 private void DetailLog(string msg, params Object[] args)
565 { 569 {
566 PhysicsScene.PhysicsLogging.Write(msg, args); 570 if (PhysicsScene.PhysicsLogging.Enabled)
571 PhysicsScene.DetailLog(msg, args);
567 } 572 }
568 573
569} 574}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
index 1ac8c59..ead6a08 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
@@ -47,6 +47,7 @@ public abstract class BSPhysObject : PhysicsActor
47 TypeName = typeName; 47 TypeName = typeName;
48 48
49 Linkset = new BSLinkset(PhysicsScene, this); 49 Linkset = new BSLinkset(PhysicsScene, this);
50 LastAssetBuildFailed = false;
50 51
51 CollisionCollection = new CollisionEventUpdate(); 52 CollisionCollection = new CollisionEventUpdate();
52 SubscribedEventsMs = 0; 53 SubscribedEventsMs = 0;
@@ -69,6 +70,23 @@ public abstract class BSPhysObject : PhysicsActor
69 // Reference to the physical shape (btCollisionShape) of this object 70 // Reference to the physical shape (btCollisionShape) of this object
70 public BulletShape BSShape; 71 public BulletShape BSShape;
71 72
73 // 'true' if the mesh's underlying asset failed to build.
74 // This will keep us from looping after the first time the build failed.
75 public bool LastAssetBuildFailed { get; set; }
76
77 // The objects base shape information. Null if not a prim type shape.
78 public PrimitiveBaseShape BaseShape { get; protected set; }
79
80 // When the physical properties are updated, an EntityProperty holds the update values.
81 // Keep the current and last EntityProperties to enable computation of differences
82 // between the current update and the previous values.
83 public EntityProperties CurrentEntityProperties { get; set; }
84 public EntityProperties LastEntityProperties { get; set; }
85
86 public abstract OMV.Vector3 Scale { get; set; }
87 public abstract bool IsSolid { get; }
88 public abstract bool IsStatic { get; }
89
72 // Stop all physical motion. 90 // Stop all physical motion.
73 public abstract void ZeroMotion(); 91 public abstract void ZeroMotion();
74 92
@@ -85,6 +103,14 @@ public abstract class BSPhysObject : PhysicsActor
85 103
86 public abstract OMV.Quaternion ForceOrientation { get; set; } 104 public abstract OMV.Quaternion ForceOrientation { get; set; }
87 105
106 public abstract OMV.Vector3 ForceVelocity { get; set; }
107
108 public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
109
110 public abstract float ForceBuoyancy { get; set; }
111
112 public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
113
88 #region Collisions 114 #region Collisions
89 115
90 // Requested number of milliseconds between collision events. Zero means disabled. 116 // Requested number of milliseconds between collision events. Zero means disabled.
@@ -125,30 +151,28 @@ public abstract class BSPhysObject : PhysicsActor
125 // if someone has subscribed for collision events.... 151 // if someone has subscribed for collision events....
126 if (SubscribedEvents()) { 152 if (SubscribedEvents()) {
127 CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); 153 CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
128 // DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}", 154 DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
129 // LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth); 155 LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
130 156
131 ret = true; 157 ret = true;
132 } 158 }
133 return ret; 159 return ret;
134 } 160 }
135 161
136 // Routine to send the collected collisions into the simulator. 162 // Send the collected collisions into the simulator.
137 // Also handles removal of this from the collection of objects with collisions if
138 // there are no collisions from this object. Mechanism is create one last
139 // collision event to make collision_end work.
140 // Called at taint time from within the Step() function thus no locking problems 163 // Called at taint time from within the Step() function thus no locking problems
141 // with CollisionCollection and ObjectsWithNoMoreCollisions. 164 // with CollisionCollection and ObjectsWithNoMoreCollisions.
142 // Return 'true' if there were some actual collisions passed up 165 // Return 'true' if there were some actual collisions passed up
143 public virtual bool SendCollisions() 166 public virtual bool SendCollisions()
144 { 167 {
145 bool ret = true; 168 bool ret = true;
169 // If the 'no collision' call, force it to happen right now so quick collision_end
170 bool force = CollisionCollection.Count == 0;
146 171
147 // throttle the collisions to the number of milliseconds specified in the subscription 172 // throttle the collisions to the number of milliseconds specified in the subscription
148 int nowTime = PhysicsScene.SimulationNowTime; 173 if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
149 if (nowTime >= NextCollisionOkTime)
150 { 174 {
151 NextCollisionOkTime = nowTime + SubscribedEventsMs; 175 NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
152 176
153 // We are called if we previously had collisions. If there are no collisions 177 // We are called if we previously had collisions. If there are no collisions
154 // this time, send up one last empty event so OpenSim can sense collision end. 178 // this time, send up one last empty event so OpenSim can sense collision end.
@@ -207,7 +231,8 @@ public abstract class BSPhysObject : PhysicsActor
207 // High performance detailed logging routine used by the physical objects. 231 // High performance detailed logging routine used by the physical objects.
208 protected void DetailLog(string msg, params Object[] args) 232 protected void DetailLog(string msg, params Object[] args)
209 { 233 {
210 PhysicsScene.PhysicsLogging.Write(msg, args); 234 if (PhysicsScene.PhysicsLogging.Enabled)
235 PhysicsScene.DetailLog(msg, args);
211 } 236 }
212} 237}
213} 238}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index f7b68ba..aeeb4dd 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -46,12 +46,10 @@ public sealed class BSPrim : BSPhysObject
46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47 private static readonly string LogHeader = "[BULLETS PRIM]"; 47 private static readonly string LogHeader = "[BULLETS PRIM]";
48 48
49 private PrimitiveBaseShape _pbs; 49 // _size is what the user passed. Scale is what we pass to the physics engine with the mesh.
50 50 // Often Scale is unity because the meshmerizer will apply _size when creating the mesh.
51 // _size is what the user passed. _scale is what we pass to the physics engine with the mesh.
52 // Often _scale is unity because the meshmerizer will apply _size when creating the mesh.
53 private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user 51 private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
54 private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer 52 // private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer
55 53
56 private bool _grabbed; 54 private bool _grabbed;
57 private bool _isSelected; 55 private bool _isSelected;
@@ -98,12 +96,12 @@ public sealed class BSPrim : BSPhysObject
98 _physicsActorType = (int)ActorTypes.Prim; 96 _physicsActorType = (int)ActorTypes.Prim;
99 _position = pos; 97 _position = pos;
100 _size = size; 98 _size = size;
101 _scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type 99 Scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type
102 _orientation = rotation; 100 _orientation = rotation;
103 _buoyancy = 1f; 101 _buoyancy = 1f;
104 _velocity = OMV.Vector3.Zero; 102 _velocity = OMV.Vector3.Zero;
105 _rotationalVelocity = OMV.Vector3.Zero; 103 _rotationalVelocity = OMV.Vector3.Zero;
106 _pbs = pbs; 104 BaseShape = pbs;
107 _isPhysical = pisPhysical; 105 _isPhysical = pisPhysical;
108 _isVolumeDetect = false; 106 _isVolumeDetect = false;
109 _friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material 107 _friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material
@@ -160,33 +158,32 @@ public sealed class BSPrim : BSPhysObject
160 get { return _size; } 158 get { return _size; }
161 set { 159 set {
162 _size = value; 160 _size = value;
163 PhysicsScene.TaintedObject("BSPrim.setSize", delegate() 161 ForceBodyShapeRebuild(false);
164 {
165 _mass = CalculateMass(); // changing size changes the mass
166 // Since _size changed, the mesh needs to be rebuilt. If rebuilt, all the correct
167 // scale and margins are set.
168 CreateGeomAndObject(true);
169 // DetailLog("{0},BSPrim.setSize,size={1},scale={2},mass={3},physical={4}", LocalID, _size, _scale, _mass, IsPhysical);
170 });
171 } 162 }
172 } 163 }
173 // Scale is what we set in the physics engine. It is different than 'size' in that 164 // Scale is what we set in the physics engine. It is different than 'size' in that
174 // 'size' can be encorporated into the mesh. In that case, the scale is <1,1,1>. 165 // 'size' can be encorporated into the mesh. In that case, the scale is <1,1,1>.
175 public OMV.Vector3 Scale 166 public override OMV.Vector3 Scale { get; set; }
176 { 167
177 get { return _scale; }
178 set { _scale = value; }
179 }
180 public override PrimitiveBaseShape Shape { 168 public override PrimitiveBaseShape Shape {
181 set { 169 set {
182 _pbs = value; 170 BaseShape = value;
183 PhysicsScene.TaintedObject("BSPrim.setShape", delegate() 171 ForceBodyShapeRebuild(false);
184 {
185 _mass = CalculateMass(); // changing the shape changes the mass
186 CreateGeomAndObject(true);
187 });
188 } 172 }
189 } 173 }
174 public override bool ForceBodyShapeRebuild(bool inTaintTime)
175 {
176 BSScene.TaintCallback rebuildOperation = delegate()
177 {
178 _mass = CalculateMass(); // changing the shape changes the mass
179 CreateGeomAndObject(true);
180 };
181 if (inTaintTime)
182 rebuildOperation();
183 else
184 PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", rebuildOperation);
185 return true;
186 }
190 public override bool Grabbed { 187 public override bool Grabbed {
191 set { _grabbed = value; 188 set { _grabbed = value;
192 } 189 }
@@ -196,7 +193,7 @@ public sealed class BSPrim : BSPhysObject
196 _isSelected = value; 193 _isSelected = value;
197 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() 194 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate()
198 { 195 {
199 // DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); 196 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
200 SetObjectDynamic(false); 197 SetObjectDynamic(false);
201 }); 198 });
202 } 199 }
@@ -265,6 +262,11 @@ public sealed class BSPrim : BSPhysObject
265 return _position; 262 return _position;
266 } 263 }
267 set { 264 set {
265 // If you must push the position into the physics engine, use ForcePosition.
266 if (_position == value)
267 {
268 return;
269 }
268 _position = value; 270 _position = value;
269 // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint? 271 // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint?
270 PositionSanityCheck(); 272 PositionSanityCheck();
@@ -320,9 +322,9 @@ public sealed class BSPrim : BSPhysObject
320 } 322 }
321 323
322 // A version of the sanity check that also makes sure a new position value is 324 // A version of the sanity check that also makes sure a new position value is
323 // pushed back to the physics engine. This routine would be used by anyone 325 // pushed to the physics engine. This routine would be used by anyone
324 // who is not already pushing the value. 326 // who is not already pushing the value.
325 private bool PositionSanityCheck2(bool atTaintTime) 327 private bool PositionSanityCheck(bool inTaintTime)
326 { 328 {
327 bool ret = false; 329 bool ret = false;
328 if (PositionSanityCheck()) 330 if (PositionSanityCheck())
@@ -332,9 +334,9 @@ public sealed class BSPrim : BSPhysObject
332 BSScene.TaintCallback sanityOperation = delegate() 334 BSScene.TaintCallback sanityOperation = delegate()
333 { 335 {
334 DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); 336 DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
335 BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); 337 ForcePosition = _position;
336 }; 338 };
337 if (atTaintTime) 339 if (inTaintTime)
338 sanityOperation(); 340 sanityOperation();
339 else 341 else
340 PhysicsScene.TaintedObject("BSPrim.PositionSanityCheck", sanityOperation); 342 PhysicsScene.TaintedObject("BSPrim.PositionSanityCheck", sanityOperation);
@@ -453,7 +455,6 @@ public sealed class BSPrim : BSPhysObject
453 } 455 }
454 return; 456 return;
455 } 457 }
456
457 public override OMV.Vector3 Velocity { 458 public override OMV.Vector3 Velocity {
458 get { return _velocity; } 459 get { return _velocity; }
459 set { 460 set {
@@ -465,6 +466,13 @@ public sealed class BSPrim : BSPhysObject
465 }); 466 });
466 } 467 }
467 } 468 }
469 public override OMV.Vector3 ForceVelocity {
470 get { return _velocity; }
471 set {
472 _velocity = value;
473 BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity);
474 }
475 }
468 public override OMV.Vector3 Torque { 476 public override OMV.Vector3 Torque {
469 get { return _torque; } 477 get { return _torque; }
470 set { _torque = value; 478 set { _torque = value;
@@ -490,6 +498,8 @@ public sealed class BSPrim : BSPhysObject
490 return _orientation; 498 return _orientation;
491 } 499 }
492 set { 500 set {
501 if (_orientation == value)
502 return;
493 _orientation = value; 503 _orientation = value;
494 // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? 504 // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint?
495 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() 505 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
@@ -534,13 +544,13 @@ public sealed class BSPrim : BSPhysObject
534 } 544 }
535 545
536 // An object is static (does not move) if selected or not physical 546 // An object is static (does not move) if selected or not physical
537 private bool IsStatic 547 public override bool IsStatic
538 { 548 {
539 get { return _isSelected || !IsPhysical; } 549 get { return _isSelected || !IsPhysical; }
540 } 550 }
541 551
542 // An object is solid if it's not phantom and if it's not doing VolumeDetect 552 // An object is solid if it's not phantom and if it's not doing VolumeDetect
543 public bool IsSolid 553 public override bool IsSolid
544 { 554 {
545 get { return !IsPhantom && !_isVolumeDetect; } 555 get { return !IsPhantom && !_isVolumeDetect; }
546 } 556 }
@@ -570,7 +580,7 @@ public sealed class BSPrim : BSPhysObject
570 // Set up the object physicalness (does gravity and collisions move this object) 580 // Set up the object physicalness (does gravity and collisions move this object)
571 MakeDynamic(IsStatic); 581 MakeDynamic(IsStatic);
572 582
573 // Update vehicle specific parameters 583 // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters)
574 _vehicle.Refresh(); 584 _vehicle.Refresh();
575 585
576 // Arrange for collision events if the simulator wants them 586 // Arrange for collision events if the simulator wants them
@@ -593,7 +603,7 @@ public sealed class BSPrim : BSPhysObject
593 // Recompute any linkset parameters. 603 // Recompute any linkset parameters.
594 // When going from non-physical to physical, this re-enables the constraints that 604 // When going from non-physical to physical, this re-enables the constraints that
595 // had been automatically disabled when the mass was set to zero. 605 // had been automatically disabled when the mass was set to zero.
596 Linkset.Refresh(this); 606 Linkset.Refresh(this, true);
597 607
598 DetailLog("{0},BSPrim.UpdatePhysicalParameters,exit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}", 608 DetailLog("{0},BSPrim.UpdatePhysicalParameters,exit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}",
599 LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, BSBody, BSShape); 609 LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, BSBody, BSShape);
@@ -618,10 +628,18 @@ public sealed class BSPrim : BSPhysObject
618 BulletSimAPI.SetMassProps2(BSBody.ptr, 0f, OMV.Vector3.Zero); 628 BulletSimAPI.SetMassProps2(BSBody.ptr, 0f, OMV.Vector3.Zero);
619 // There is no inertia in a static object 629 // There is no inertia in a static object
620 BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr); 630 BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr);
631 // Set collision detection parameters
632 if (PhysicsScene.Params.ccdMotionThreshold > 0f)
633 {
634 BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
635 BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
636 }
621 // There can be special things needed for implementing linksets 637 // There can be special things needed for implementing linksets
622 Linkset.MakeStatic(this); 638 Linkset.MakeStatic(this);
623 // The activation state is 'disabled' so Bullet will not try to act on it 639 // The activation state is 'disabled' so Bullet will not try to act on it.
624 BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.DISABLE_SIMULATION); 640 BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.DISABLE_SIMULATION);
641 // Start it out sleeping and physical actions could wake it up.
642 // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING);
625 643
626 BSBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter; 644 BSBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter;
627 BSBody.collisionMask = CollisionFilterGroups.StaticObjectMask; 645 BSBody.collisionMask = CollisionFilterGroups.StaticObjectMask;
@@ -638,12 +656,22 @@ public sealed class BSPrim : BSPhysObject
638 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 656 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
639 BulletSimAPI.ClearAllForces2(BSBody.ptr); 657 BulletSimAPI.ClearAllForces2(BSBody.ptr);
640 658
659 // For good measure, make sure the transform is set through to the motion state
660 BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
661
641 // A dynamic object has mass 662 // A dynamic object has mass
642 IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.ptr); 663 IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.ptr);
643 OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Mass); 664 OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Mass);
644 BulletSimAPI.SetMassProps2(BSBody.ptr, _mass, inertia); 665 BulletSimAPI.SetMassProps2(BSBody.ptr, _mass, inertia);
645 BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr); 666 BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr);
646 667
668 // Set collision detection parameters
669 if (PhysicsScene.Params.ccdMotionThreshold > 0f)
670 {
671 BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
672 BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
673 }
674
647 // Various values for simulation limits 675 // Various values for simulation limits
648 BulletSimAPI.SetDamping2(BSBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping); 676 BulletSimAPI.SetDamping2(BSBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping);
649 BulletSimAPI.SetDeactivationTime2(BSBody.ptr, PhysicsScene.Params.deactivationTime); 677 BulletSimAPI.SetDeactivationTime2(BSBody.ptr, PhysicsScene.Params.deactivationTime);
@@ -655,8 +683,8 @@ public sealed class BSPrim : BSPhysObject
655 683
656 // Force activation of the object so Bullet will act on it. 684 // Force activation of the object so Bullet will act on it.
657 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. 685 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
658 BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); 686 BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
659 BulletSimAPI.Activate2(BSBody.ptr, true); 687 // BulletSimAPI.Activate2(BSBody.ptr, true);
660 688
661 BSBody.collisionFilter = CollisionFilterGroups.ObjectFilter; 689 BSBody.collisionFilter = CollisionFilterGroups.ObjectFilter;
662 BSBody.collisionMask = CollisionFilterGroups.ObjectMask; 690 BSBody.collisionMask = CollisionFilterGroups.ObjectMask;
@@ -774,6 +802,15 @@ public sealed class BSPrim : BSPhysObject
774 }); 802 });
775 } 803 }
776 } 804 }
805 public override OMV.Vector3 ForceRotationalVelocity {
806 get {
807 return _rotationalVelocity;
808 }
809 set {
810 _rotationalVelocity = value;
811 BulletSimAPI.SetAngularVelocity2(BSBody.ptr, _rotationalVelocity);
812 }
813 }
777 public override bool Kinematic { 814 public override bool Kinematic {
778 get { return _kinematic; } 815 get { return _kinematic; }
779 set { _kinematic = value; 816 set { _kinematic = value;
@@ -786,13 +823,20 @@ public sealed class BSPrim : BSPhysObject
786 _buoyancy = value; 823 _buoyancy = value;
787 PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate() 824 PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate()
788 { 825 {
789 // DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 826 ForceBuoyancy = _buoyancy;
790 // Buoyancy is faked by changing the gravity applied to the object
791 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
792 BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav));
793 }); 827 });
794 } 828 }
795 } 829 }
830 public override float ForceBuoyancy {
831 get { return _buoyancy; }
832 set {
833 _buoyancy = value;
834 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
835 // Buoyancy is faked by changing the gravity applied to the object
836 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
837 BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav));
838 }
839 }
796 840
797 // Used for MoveTo 841 // Used for MoveTo
798 public override OMV.Vector3 PIDTarget { 842 public override OMV.Vector3 PIDTarget {
@@ -828,6 +872,9 @@ public sealed class BSPrim : BSPhysObject
828 872
829 private List<OMV.Vector3> m_accumulatedForces = new List<OMV.Vector3>(); 873 private List<OMV.Vector3> m_accumulatedForces = new List<OMV.Vector3>();
830 public override void AddForce(OMV.Vector3 force, bool pushforce) { 874 public override void AddForce(OMV.Vector3 force, bool pushforce) {
875 AddForce(force, pushforce, false);
876 }
877 public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
831 // for an object, doesn't matter if force is a pushforce or not 878 // for an object, doesn't matter if force is a pushforce or not
832 if (force.IsFinite()) 879 if (force.IsFinite())
833 { 880 {
@@ -840,11 +887,12 @@ public sealed class BSPrim : BSPhysObject
840 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); 887 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
841 return; 888 return;
842 } 889 }
843 PhysicsScene.TaintedObject("BSPrim.AddForce", delegate() 890 BSScene.TaintCallback addForceOperation = delegate()
844 { 891 {
845 OMV.Vector3 fSum = OMV.Vector3.Zero; 892 OMV.Vector3 fSum = OMV.Vector3.Zero;
846 lock (m_accumulatedForces) 893 lock (m_accumulatedForces)
847 { 894 {
895 // Sum the accumulated additional forces for one big force to apply once.
848 foreach (OMV.Vector3 v in m_accumulatedForces) 896 foreach (OMV.Vector3 v in m_accumulatedForces)
849 { 897 {
850 fSum += v; 898 fSum += v;
@@ -854,7 +902,11 @@ public sealed class BSPrim : BSPhysObject
854 // DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum); 902 // DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum);
855 // For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object. 903 // For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object.
856 BulletSimAPI.ApplyCentralForce2(BSBody.ptr, fSum); 904 BulletSimAPI.ApplyCentralForce2(BSBody.ptr, fSum);
857 }); 905 };
906 if (inTaintTime)
907 addForceOperation();
908 else
909 PhysicsScene.TaintedObject("BSPrim.AddForce", addForceOperation);
858 } 910 }
859 911
860 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { 912 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
@@ -872,19 +924,19 @@ public sealed class BSPrim : BSPhysObject
872 float tmp; 924 float tmp;
873 925
874 float returnMass = 0; 926 float returnMass = 0;
875 float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f; 927 float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f;
876 float hollowVolume = hollowAmount * hollowAmount; 928 float hollowVolume = hollowAmount * hollowAmount;
877 929
878 switch (_pbs.ProfileShape) 930 switch (BaseShape.ProfileShape)
879 { 931 {
880 case ProfileShape.Square: 932 case ProfileShape.Square:
881 // default box 933 // default box
882 934
883 if (_pbs.PathCurve == (byte)Extrusion.Straight) 935 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
884 { 936 {
885 if (hollowAmount > 0.0) 937 if (hollowAmount > 0.0)
886 { 938 {
887 switch (_pbs.HollowShape) 939 switch (BaseShape.HollowShape)
888 { 940 {
889 case HollowShape.Square: 941 case HollowShape.Square:
890 case HollowShape.Same: 942 case HollowShape.Same:
@@ -908,19 +960,19 @@ public sealed class BSPrim : BSPhysObject
908 } 960 }
909 } 961 }
910 962
911 else if (_pbs.PathCurve == (byte)Extrusion.Curve1) 963 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
912 { 964 {
913 //a tube 965 //a tube
914 966
915 volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX); 967 volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX);
916 tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY); 968 tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY);
917 volume -= volume*tmp*tmp; 969 volume -= volume*tmp*tmp;
918 970
919 if (hollowAmount > 0.0) 971 if (hollowAmount > 0.0)
920 { 972 {
921 hollowVolume *= hollowAmount; 973 hollowVolume *= hollowAmount;
922 974
923 switch (_pbs.HollowShape) 975 switch (BaseShape.HollowShape)
924 { 976 {
925 case HollowShape.Square: 977 case HollowShape.Square:
926 case HollowShape.Same: 978 case HollowShape.Same:
@@ -945,13 +997,13 @@ public sealed class BSPrim : BSPhysObject
945 997
946 case ProfileShape.Circle: 998 case ProfileShape.Circle:
947 999
948 if (_pbs.PathCurve == (byte)Extrusion.Straight) 1000 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
949 { 1001 {
950 volume *= 0.78539816339f; // elipse base 1002 volume *= 0.78539816339f; // elipse base
951 1003
952 if (hollowAmount > 0.0) 1004 if (hollowAmount > 0.0)
953 { 1005 {
954 switch (_pbs.HollowShape) 1006 switch (BaseShape.HollowShape)
955 { 1007 {
956 case HollowShape.Same: 1008 case HollowShape.Same:
957 case HollowShape.Circle: 1009 case HollowShape.Circle:
@@ -973,10 +1025,10 @@ public sealed class BSPrim : BSPhysObject
973 } 1025 }
974 } 1026 }
975 1027
976 else if (_pbs.PathCurve == (byte)Extrusion.Curve1) 1028 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
977 { 1029 {
978 volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX); 1030 volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX);
979 tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); 1031 tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
980 volume *= (1.0f - tmp * tmp); 1032 volume *= (1.0f - tmp * tmp);
981 1033
982 if (hollowAmount > 0.0) 1034 if (hollowAmount > 0.0)
@@ -985,7 +1037,7 @@ public sealed class BSPrim : BSPhysObject
985 // calculate the hollow volume by it's shape compared to the prim shape 1037 // calculate the hollow volume by it's shape compared to the prim shape
986 hollowVolume *= hollowAmount; 1038 hollowVolume *= hollowAmount;
987 1039
988 switch (_pbs.HollowShape) 1040 switch (BaseShape.HollowShape)
989 { 1041 {
990 case HollowShape.Same: 1042 case HollowShape.Same:
991 case HollowShape.Circle: 1043 case HollowShape.Circle:
@@ -1009,7 +1061,7 @@ public sealed class BSPrim : BSPhysObject
1009 break; 1061 break;
1010 1062
1011 case ProfileShape.HalfCircle: 1063 case ProfileShape.HalfCircle:
1012 if (_pbs.PathCurve == (byte)Extrusion.Curve1) 1064 if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1013 { 1065 {
1014 volume *= 0.52359877559829887307710723054658f; 1066 volume *= 0.52359877559829887307710723054658f;
1015 } 1067 }
@@ -1017,7 +1069,7 @@ public sealed class BSPrim : BSPhysObject
1017 1069
1018 case ProfileShape.EquilateralTriangle: 1070 case ProfileShape.EquilateralTriangle:
1019 1071
1020 if (_pbs.PathCurve == (byte)Extrusion.Straight) 1072 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
1021 { 1073 {
1022 volume *= 0.32475953f; 1074 volume *= 0.32475953f;
1023 1075
@@ -1025,7 +1077,7 @@ public sealed class BSPrim : BSPhysObject
1025 { 1077 {
1026 1078
1027 // calculate the hollow volume by it's shape compared to the prim shape 1079 // calculate the hollow volume by it's shape compared to the prim shape
1028 switch (_pbs.HollowShape) 1080 switch (BaseShape.HollowShape)
1029 { 1081 {
1030 case HollowShape.Same: 1082 case HollowShape.Same:
1031 case HollowShape.Triangle: 1083 case HollowShape.Triangle:
@@ -1050,11 +1102,11 @@ public sealed class BSPrim : BSPhysObject
1050 volume *= (1.0f - hollowVolume); 1102 volume *= (1.0f - hollowVolume);
1051 } 1103 }
1052 } 1104 }
1053 else if (_pbs.PathCurve == (byte)Extrusion.Curve1) 1105 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1054 { 1106 {
1055 volume *= 0.32475953f; 1107 volume *= 0.32475953f;
1056 volume *= 0.01f * (float)(200 - _pbs.PathScaleX); 1108 volume *= 0.01f * (float)(200 - BaseShape.PathScaleX);
1057 tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); 1109 tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
1058 volume *= (1.0f - tmp * tmp); 1110 volume *= (1.0f - tmp * tmp);
1059 1111
1060 if (hollowAmount > 0.0) 1112 if (hollowAmount > 0.0)
@@ -1062,7 +1114,7 @@ public sealed class BSPrim : BSPhysObject
1062 1114
1063 hollowVolume *= hollowAmount; 1115 hollowVolume *= hollowAmount;
1064 1116
1065 switch (_pbs.HollowShape) 1117 switch (BaseShape.HollowShape)
1066 { 1118 {
1067 case HollowShape.Same: 1119 case HollowShape.Same:
1068 case HollowShape.Triangle: 1120 case HollowShape.Triangle:
@@ -1102,26 +1154,26 @@ public sealed class BSPrim : BSPhysObject
1102 float profileBegin; 1154 float profileBegin;
1103 float profileEnd; 1155 float profileEnd;
1104 1156
1105 if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible) 1157 if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible)
1106 { 1158 {
1107 taperX1 = _pbs.PathScaleX * 0.01f; 1159 taperX1 = BaseShape.PathScaleX * 0.01f;
1108 if (taperX1 > 1.0f) 1160 if (taperX1 > 1.0f)
1109 taperX1 = 2.0f - taperX1; 1161 taperX1 = 2.0f - taperX1;
1110 taperX = 1.0f - taperX1; 1162 taperX = 1.0f - taperX1;
1111 1163
1112 taperY1 = _pbs.PathScaleY * 0.01f; 1164 taperY1 = BaseShape.PathScaleY * 0.01f;
1113 if (taperY1 > 1.0f) 1165 if (taperY1 > 1.0f)
1114 taperY1 = 2.0f - taperY1; 1166 taperY1 = 2.0f - taperY1;
1115 taperY = 1.0f - taperY1; 1167 taperY = 1.0f - taperY1;
1116 } 1168 }
1117 else 1169 else
1118 { 1170 {
1119 taperX = _pbs.PathTaperX * 0.01f; 1171 taperX = BaseShape.PathTaperX * 0.01f;
1120 if (taperX < 0.0f) 1172 if (taperX < 0.0f)
1121 taperX = -taperX; 1173 taperX = -taperX;
1122 taperX1 = 1.0f - taperX; 1174 taperX1 = 1.0f - taperX;
1123 1175
1124 taperY = _pbs.PathTaperY * 0.01f; 1176 taperY = BaseShape.PathTaperY * 0.01f;
1125 if (taperY < 0.0f) 1177 if (taperY < 0.0f)
1126 taperY = -taperY; 1178 taperY = -taperY;
1127 taperY1 = 1.0f - taperY; 1179 taperY1 = 1.0f - taperY;
@@ -1131,13 +1183,13 @@ public sealed class BSPrim : BSPhysObject
1131 1183
1132 volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); 1184 volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY);
1133 1185
1134 pathBegin = (float)_pbs.PathBegin * 2.0e-5f; 1186 pathBegin = (float)BaseShape.PathBegin * 2.0e-5f;
1135 pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f; 1187 pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f;
1136 volume *= (pathEnd - pathBegin); 1188 volume *= (pathEnd - pathBegin);
1137 1189
1138 // this is crude aproximation 1190 // this is crude aproximation
1139 profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f; 1191 profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f;
1140 profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f; 1192 profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
1141 volume *= (profileEnd - profileBegin); 1193 volume *= (profileEnd - profileBegin);
1142 1194
1143 returnMass = _density * volume; 1195 returnMass = _density * volume;
@@ -1172,7 +1224,8 @@ public sealed class BSPrim : BSPhysObject
1172 shape.Position = _position; 1224 shape.Position = _position;
1173 shape.Rotation = _orientation; 1225 shape.Rotation = _orientation;
1174 shape.Velocity = _velocity; 1226 shape.Velocity = _velocity;
1175 shape.Scale = _scale; 1227 shape.Size = _size;
1228 shape.Scale = Scale;
1176 shape.Mass = _isPhysical ? _mass : 0f; 1229 shape.Mass = _isPhysical ? _mass : 0f;
1177 shape.Buoyancy = _buoyancy; 1230 shape.Buoyancy = _buoyancy;
1178 shape.HullKey = 0; 1231 shape.HullKey = 0;
@@ -1182,7 +1235,6 @@ public sealed class BSPrim : BSPhysObject
1182 shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse; 1235 shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse;
1183 shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue; 1236 shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
1184 shape.Solid = IsSolid ? ShapeData.numericFalse : ShapeData.numericTrue; 1237 shape.Solid = IsSolid ? ShapeData.numericFalse : ShapeData.numericTrue;
1185 shape.Size = _size;
1186 } 1238 }
1187 // Rebuild the geometry and object. 1239 // Rebuild the geometry and object.
1188 // This is called when the shape changes so we need to recreate the mesh/hull. 1240 // This is called when the shape changes so we need to recreate the mesh/hull.
@@ -1199,11 +1251,12 @@ public sealed class BSPrim : BSPhysObject
1199 // Create the correct physical representation for this type of object. 1251 // Create the correct physical representation for this type of object.
1200 // Updates BSBody and BSShape with the new information. 1252 // Updates BSBody and BSShape with the new information.
1201 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. 1253 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
1202 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, _pbs, 1254 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, BaseShape,
1203 null, delegate(BulletBody dBody) 1255 null, delegate(BulletBody dBody)
1204 { 1256 {
1205 // Called if the current prim body is about to be destroyed. 1257 // Called if the current prim body is about to be destroyed.
1206 // Remove all the physical dependencies on the old body. 1258 // Remove all the physical dependencies on the old body.
1259 // (Maybe someday make the changing of BSShape an event handled by BSLinkset.)
1207 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); 1260 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this);
1208 }); 1261 });
1209 1262
@@ -1292,7 +1345,11 @@ public sealed class BSPrim : BSPhysObject
1292 _acceleration = entprop.Acceleration; 1345 _acceleration = entprop.Acceleration;
1293 _rotationalVelocity = entprop.RotationalVelocity; 1346 _rotationalVelocity = entprop.RotationalVelocity;
1294 1347
1295 PositionSanityCheck2(true); 1348 // remember the current and last set values
1349 LastEntityProperties = CurrentEntityProperties;
1350 CurrentEntityProperties = entprop;
1351
1352 PositionSanityCheck(true);
1296 1353
1297 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", 1354 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
1298 LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); 1355 LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
@@ -1304,12 +1361,15 @@ public sealed class BSPrim : BSPhysObject
1304 /* 1361 /*
1305 else 1362 else
1306 { 1363 {
1307 // For debugging, we can also report the movement of children 1364 // For debugging, report the movement of children
1308 DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", 1365 DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
1309 LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, 1366 LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
1310 entprop.Acceleration, entprop.RotationalVelocity); 1367 entprop.Acceleration, entprop.RotationalVelocity);
1311 } 1368 }
1312 */ 1369 */
1370 // The linkset implimentation might want to know about this.
1371
1372 Linkset.UpdateProperties(this);
1313 } 1373 }
1314} 1374}
1315} 1375}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index aaed7de..6621d39 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -39,7 +39,6 @@ using log4net;
39using OpenMetaverse; 39using OpenMetaverse;
40 40
41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) 41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
42// Move all logic out of the C++ code and into the C# code for easier future modifications.
43// Test sculpties (verified that they don't work) 42// Test sculpties (verified that they don't work)
44// Compute physics FPS reasonably 43// Compute physics FPS reasonably
45// Based on material, set density and friction 44// Based on material, set density and friction
@@ -90,10 +89,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
90 // let my minuions use my logger 89 // let my minuions use my logger
91 public ILog Logger { get { return m_log; } } 90 public ILog Logger { get { return m_log; } }
92 91
93 // If non-zero, the number of simulation steps between calls to the physics
94 // engine to output detailed physics stats. Debug logging level must be on also.
95 private int m_detailedStatsStep = 0;
96
97 public IMesher mesher; 92 public IMesher mesher;
98 // Level of Detail values kept as float because that's what the Meshmerizer wants 93 // Level of Detail values kept as float because that's what the Meshmerizer wants
99 public float MeshLOD { get; private set; } 94 public float MeshLOD { get; private set; }
@@ -112,6 +107,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
112 private float m_fixedTimeStep; 107 private float m_fixedTimeStep;
113 private long m_simulationStep = 0; 108 private long m_simulationStep = 0;
114 public long SimulationStep { get { return m_simulationStep; } } 109 public long SimulationStep { get { return m_simulationStep; } }
110 private int m_taintsToProcessPerStep;
115 111
116 // A value of the time now so all the collision and update routines do not have to get their own 112 // A value of the time now so all the collision and update routines do not have to get their own
117 // Set to 'now' just before all the prims and actors are called for collisions and updates 113 // 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
131 127
132 public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed 128 public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
133 public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes 129 public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
130 public bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects
134 131
135 public float PID_D { get; private set; } // derivative 132 public float PID_D { get; private set; } // derivative
136 public float PID_P { get; private set; } // proportional 133 public float PID_P { get; private set; } // proportional
@@ -254,19 +251,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters
254 251
255 // The bounding box for the simulated world. The origin is 0,0,0 unless we're 252 // The bounding box for the simulated world. The origin is 0,0,0 unless we're
256 // a child in a mega-region. 253 // a child in a mega-region.
257 // Turns out that Bullet really doesn't care about the extents of the simulated 254 // Bullet actually doesn't care about the extents of the simulated
258 // area. It tracks active objects no matter where they are. 255 // area. It tracks active objects no matter where they are.
259 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); 256 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
260 257
261 // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); 258 // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
262 WorldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(), 259 World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
263 m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), 260 m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
264 m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(), 261 m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(),
265 m_DebugLogCallbackHandle); 262 m_DebugLogCallbackHandle));
266
267 // Initialization to support the transition to a new API which puts most of the logic
268 // into the C# code so it is easier to modify and add to.
269 World = new BulletSim(WorldID, this, BulletSimAPI.GetSimHandle2(WorldID));
270 263
271 Constraints = new BSConstraintCollection(World); 264 Constraints = new BSConstraintCollection(World);
272 265
@@ -331,7 +324,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
331 // Called directly from unmanaged code so don't do much 324 // Called directly from unmanaged code so don't do much
332 private void BulletLoggerPhysLog(string msg) 325 private void BulletLoggerPhysLog(string msg)
333 { 326 {
334 PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg); 327 DetailLog("[BULLETS UNMANAGED]:" + msg);
335 } 328 }
336 329
337 public override void Dispose() 330 public override void Dispose()
@@ -363,7 +356,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
363 } 356 }
364 357
365 // Anything left in the unmanaged code should be cleaned out 358 // Anything left in the unmanaged code should be cleaned out
366 BulletSimAPI.Shutdown(WorldID); 359 BulletSimAPI.Shutdown2(World.ptr);
367 360
368 // Not logging any more 361 // Not logging any more
369 PhysicsLogging.Close(); 362 PhysicsLogging.Close();
@@ -494,19 +487,19 @@ public class BSScene : PhysicsScene, IPhysicsParameters
494 m_simulationStep++; 487 m_simulationStep++;
495 int numSubSteps = 0; 488 int numSubSteps = 0;
496 489
497 // Sometimes needed for debugging to find out what happened before the step 490 // DEBUG
498 // PhysicsLogging.Flush(); 491 // DetailLog("{0},BSScene.Simulate,beforeStep,ntaimts={1},step={2}", DetailLogZero, numTaints, m_simulationStep);
499 492
500 try 493 try
501 { 494 {
502 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); 495 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
503 496
504 numSubSteps = BulletSimAPI.PhysicsStep(WorldID, timeStep, m_maxSubSteps, m_fixedTimeStep, 497 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
505 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); 498 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
506 499
507 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); 500 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
508 DetailLog("{0},Simulate,call, nTaints={1}, simTime={2}, substeps={3}, updates={4}, colliders={5}", 501 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}",
509 DetailLogZero, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); 502 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount);
510 } 503 }
511 catch (Exception e) 504 catch (Exception e)
512 { 505 {
@@ -539,26 +532,26 @@ public class BSScene : PhysicsScene, IPhysicsParameters
539 } 532 }
540 } 533 }
541 534
542 // This is a kludge to get avatar movement updates.
543 // the simulator expects collisions for avatars even if there are have been no collisions. This updates
544 // avatar animations and stuff.
545 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
546 foreach (BSPhysObject bsp in m_avatars)
547 bsp.SendCollisions();
548
549 // The above SendCollision's batch up the collisions on the objects. 535 // The above SendCollision's batch up the collisions on the objects.
550 // Now push the collisions into the simulator. 536 // Now push the collisions into the simulator.
551 if (ObjectsWithCollisions.Count > 0) 537 if (ObjectsWithCollisions.Count > 0)
552 { 538 {
553 foreach (BSPhysObject bsp in ObjectsWithCollisions) 539 foreach (BSPhysObject bsp in ObjectsWithCollisions)
554 if (!m_avatars.Contains(bsp)) // don't call avatars twice 540 if (!bsp.SendCollisions())
555 if (!bsp.SendCollisions()) 541 {
556 { 542 // If the object is done colliding, see that it's removed from the colliding list
557 // If the object is done colliding, see that it's removed from the colliding list 543 ObjectsWithNoMoreCollisions.Add(bsp);
558 ObjectsWithNoMoreCollisions.Add(bsp); 544 }
559 }
560 } 545 }
561 546
547 // This is a kludge to get avatar movement updates.
548 // The simulator expects collisions for avatars even if there are have been no collisions.
549 // The event updates avatar animations and stuff.
550 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
551 foreach (BSPhysObject bsp in m_avatars)
552 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
553 bsp.SendCollisions();
554
562 // Objects that are done colliding are removed from the ObjectsWithCollisions list. 555 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
563 // Not done above because it is inside an iteration of ObjectWithCollisions. 556 // Not done above because it is inside an iteration of ObjectWithCollisions.
564 if (ObjectsWithNoMoreCollisions.Count > 0) 557 if (ObjectsWithNoMoreCollisions.Count > 0)
@@ -582,19 +575,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters
582 } 575 }
583 } 576 }
584 577
585 // If enabled, call into the physics engine to dump statistics 578 // This causes the unmanaged code to output ALL the values found in ALL the objects in the world.
586 if (m_detailedStatsStep > 0) 579 // Only enable this in a limited test world with few objects.
587 { 580 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
588 if ((m_simulationStep % m_detailedStatsStep) == 0)
589 {
590 BulletSimAPI.DumpBulletStatistics();
591 }
592 }
593 581
594 // The physics engine returns the number of milliseconds it simulated this call. 582 // The physics engine returns the number of milliseconds it simulated this call.
595 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. 583 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
596 // Since Bullet normally does 5 or 6 substeps, this will normally sum to about 60 FPS. 584 // We multiply by 55 to give a recognizable running rate (55 or less).
597 return numSubSteps * m_fixedTimeStep * 1000; 585 return numSubSteps * m_fixedTimeStep * 1000 * 55;
586 // return timeStep * 1000 * 55;
598 } 587 }
599 588
600 // Something has collided 589 // Something has collided
@@ -617,7 +606,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
617 BSPhysObject collidee = null; 606 BSPhysObject collidee = null;
618 PhysObjects.TryGetValue(collidingWith, out collidee); 607 PhysObjects.TryGetValue(collidingWith, out collidee);
619 608
620 DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); 609 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
621 610
622 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) 611 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
623 { 612 {
@@ -704,6 +693,35 @@ public class BSScene : PhysicsScene, IPhysicsParameters
704 if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process 693 if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process
705 { 694 {
706 // swizzle a new list into the list location so we can process what's there 695 // swizzle a new list into the list location so we can process what's there
696 int taintCount = m_taintsToProcessPerStep;
697 TaintCallbackEntry oneCallback = new TaintCallbackEntry();
698 while (_taintedObjects.Count > 0 && taintCount-- > 0)
699 {
700 bool gotOne = false;
701 lock (_taintLock)
702 {
703 if (_taintedObjects.Count > 0)
704 {
705 oneCallback = _taintedObjects[0];
706 _taintedObjects.RemoveAt(0);
707 gotOne = true;
708 }
709 }
710 if (gotOne)
711 {
712 try
713 {
714 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG
715 oneCallback.callback();
716 }
717 catch (Exception e)
718 {
719 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e);
720 }
721 }
722 }
723 /*
724 // swizzle a new list into the list location so we can process what's there
707 List<TaintCallbackEntry> oldList; 725 List<TaintCallbackEntry> oldList;
708 lock (_taintLock) 726 lock (_taintLock)
709 { 727 {
@@ -715,6 +733,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
715 { 733 {
716 try 734 try
717 { 735 {
736 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
718 tcbe.callback(); 737 tcbe.callback();
719 } 738 }
720 catch (Exception e) 739 catch (Exception e)
@@ -723,6 +742,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
723 } 742 }
724 } 743 }
725 oldList.Clear(); 744 oldList.Clear();
745 */
726 } 746 }
727 } 747 }
728 748
@@ -780,6 +800,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
780 delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); 800 delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
781 delegate float ParamGet(BSScene scene); 801 delegate float ParamGet(BSScene scene);
782 delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); 802 delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
803 delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
783 804
784 private struct ParameterDefn 805 private struct ParameterDefn
785 { 806 {
@@ -789,6 +810,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
789 public ParamUser userParam; // get the value from the configuration file 810 public ParamUser userParam; // get the value from the configuration file
790 public ParamGet getter; // return the current value stored for this parameter 811 public ParamGet getter; // return the current value stored for this parameter
791 public ParamSet setter; // set the current value for this parameter 812 public ParamSet setter; // set the current value for this parameter
813 public SetOnObject onObject; // set the value on an object in the physical domain
792 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) 814 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
793 { 815 {
794 name = n; 816 name = n;
@@ -797,6 +819,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters
797 userParam = u; 819 userParam = u;
798 getter = g; 820 getter = g;
799 setter = s; 821 setter = s;
822 onObject = null;
823 }
824 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
825 {
826 name = n;
827 desc = d;
828 defaultValue = v;
829 userParam = u;
830 getter = g;
831 setter = s;
832 onObject = o;
800 } 833 }
801 } 834 }
802 835
@@ -818,6 +851,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
818 // 851 //
819 // The single letter parameters for the delegates are: 852 // The single letter parameters for the delegates are:
820 // s = BSScene 853 // s = BSScene
854 // o = BSPhysObject
821 // p = string parameter name 855 // p = string parameter name
822 // l = localID of referenced object 856 // l = localID of referenced object
823 // v = float value 857 // v = float value
@@ -834,6 +868,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
834 (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, 868 (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
835 (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); }, 869 (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); },
836 (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ), 870 (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ),
871 new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
872 ConfigurationParameters.numericTrue,
873 (s,cf,p,v) => { s.ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, s.BoolNumeric(v)); },
874 (s) => { return s.NumericBool(s.ShouldUseHullsForPhysicalObjects); },
875 (s,p,l,v) => { s.ShouldUseHullsForPhysicalObjects = s.BoolNumeric(v); } ),
837 876
838 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", 877 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
839 8f, 878 8f,
@@ -876,6 +915,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
876 (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, 915 (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
877 (s) => { return (float)s.m_maxUpdatesPerFrame; }, 916 (s) => { return (float)s.m_maxUpdatesPerFrame; },
878 (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), 917 (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
918 new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
919 100f,
920 (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
921 (s) => { return (float)s.m_taintsToProcessPerStep; },
922 (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
879 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", 923 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
880 10000.01f, 924 10000.01f,
881 (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); }, 925 (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); },
@@ -917,70 +961,84 @@ public class BSScene : PhysicsScene, IPhysicsParameters
917 -9.80665f, 961 -9.80665f,
918 (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); }, 962 (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); },
919 (s) => { return s.m_params[0].gravity; }, 963 (s) => { return s.m_params[0].gravity; },
920 (s,p,l,v) => { s.m_params[0].gravity = v; s.TaintedUpdateParameter(p,l,v); } ), 964 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); },
965 (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
921 966
922 967
923 new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", 968 new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
924 0f, 969 0f,
925 (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, 970 (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); },
926 (s) => { return s.m_params[0].linearDamping; }, 971 (s) => { return s.m_params[0].linearDamping; },
927 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); } ), 972 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); },
973 (s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ),
928 new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", 974 new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
929 0f, 975 0f,
930 (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, 976 (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); },
931 (s) => { return s.m_params[0].angularDamping; }, 977 (s) => { return s.m_params[0].angularDamping; },
932 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); } ), 978 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); },
979 (s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ),
933 new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", 980 new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
934 0.2f, 981 0.2f,
935 (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, 982 (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); },
936 (s) => { return s.m_params[0].deactivationTime; }, 983 (s) => { return s.m_params[0].deactivationTime; },
937 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); } ), 984 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); },
985 (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.BSBody.ptr, v); } ),
938 new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", 986 new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
939 0.8f, 987 0.8f,
940 (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, 988 (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); },
941 (s) => { return s.m_params[0].linearSleepingThreshold; }, 989 (s) => { return s.m_params[0].linearSleepingThreshold; },
942 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); } ), 990 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); },
991 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ),
943 new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", 992 new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
944 1.0f, 993 1.0f,
945 (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, 994 (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); },
946 (s) => { return s.m_params[0].angularSleepingThreshold; }, 995 (s) => { return s.m_params[0].angularSleepingThreshold; },
947 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); } ), 996 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); },
997 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ),
948 new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , 998 new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
949 0f, // set to zero to disable 999 0f, // set to zero to disable
950 (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, 1000 (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); },
951 (s) => { return s.m_params[0].ccdMotionThreshold; }, 1001 (s) => { return s.m_params[0].ccdMotionThreshold; },
952 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); } ), 1002 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); },
1003 (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.BSBody.ptr, v); } ),
953 new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , 1004 new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
954 0f, 1005 0f,
955 (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, 1006 (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); },
956 (s) => { return s.m_params[0].ccdSweptSphereRadius; }, 1007 (s) => { return s.m_params[0].ccdSweptSphereRadius; },
957 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); } ), 1008 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); },
1009 (s,o,v) => { BulletSimAPI.SetCcdSweepSphereRadius2(o.BSBody.ptr, v); } ),
958 new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , 1010 new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
959 0.1f, 1011 0.1f,
960 (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, 1012 (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); },
961 (s) => { return s.m_params[0].contactProcessingThreshold; }, 1013 (s) => { return s.m_params[0].contactProcessingThreshold; },
962 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); } ), 1014 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); },
1015 (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.BSBody.ptr, v); } ),
963 1016
964 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , 1017 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
965 0.5f, 1018 0.5f,
966 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, 1019 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
967 (s) => { return s.m_params[0].terrainFriction; }, 1020 (s) => { return s.m_params[0].terrainFriction; },
968 (s,p,l,v) => { s.m_params[0].terrainFriction = v; s.TaintedUpdateParameter(p,l,v); } ), 1021 (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ),
969 new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , 1022 new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
970 0.8f, 1023 0.8f,
971 (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); }, 1024 (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); },
972 (s) => { return s.m_params[0].terrainHitFraction; }, 1025 (s) => { return s.m_params[0].terrainHitFraction; },
973 (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; s.TaintedUpdateParameter(p,l,v); } ), 1026 (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ),
974 new ParameterDefn("TerrainRestitution", "Bouncyness" , 1027 new ParameterDefn("TerrainRestitution", "Bouncyness" ,
975 0f, 1028 0f,
976 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, 1029 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
977 (s) => { return s.m_params[0].terrainRestitution; }, 1030 (s) => { return s.m_params[0].terrainRestitution; },
978 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; s.TaintedUpdateParameter(p,l,v); } ), 1031 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ),
979 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", 1032 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
980 0.2f, 1033 0.2f,
981 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, 1034 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
982 (s) => { return s.m_params[0].avatarFriction; }, 1035 (s) => { return s.m_params[0].avatarFriction; },
983 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), 1036 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ),
1037 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
1038 10f,
1039 (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
1040 (s) => { return s.m_params[0].avatarStandingFriction; },
1041 (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
984 new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", 1042 new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
985 60f, 1043 60f,
986 (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); }, 1044 (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); },
@@ -1070,12 +1128,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1070 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, 1128 (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
1071 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), 1129 (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
1072 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", 1130 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
1073 0.1f, 1131 0.001f,
1074 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, 1132 (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
1075 (s) => { return s.m_params[0].linkConstraintCFM; }, 1133 (s) => { return s.m_params[0].linkConstraintCFM; },
1076 (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), 1134 (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ),
1077 new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", 1135 new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
1078 0.2f, 1136 0.8f,
1079 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, 1137 (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
1080 (s) => { return s.m_params[0].linkConstraintERP; }, 1138 (s) => { return s.m_params[0].linkConstraintERP; },
1081 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), 1139 (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
@@ -1085,11 +1143,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1085 (s) => { return s.m_params[0].linkConstraintSolverIterations; }, 1143 (s) => { return s.m_params[0].linkConstraintSolverIterations; },
1086 (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ), 1144 (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ),
1087 1145
1088 new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)", 1146 new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)",
1089 0f, 1147 0f,
1090 (s,cf,p,v) => { s.m_detailedStatsStep = cf.GetInt(p, (int)v); }, 1148 (s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
1091 (s) => { return (float)s.m_detailedStatsStep; }, 1149 (s) => { return (float)s.m_params[0].physicsLoggingFrames; },
1092 (s,p,l,v) => { s.m_detailedStatsStep = (int)v; } ), 1150 (s,p,l,v) => { s.m_params[0].physicsLoggingFrames = (int)v; } ),
1093 }; 1151 };
1094 1152
1095 // Convert a boolean to our numeric true and false values 1153 // Convert a boolean to our numeric true and false values
@@ -1197,52 +1255,54 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1197 return ret; 1255 return ret;
1198 } 1256 }
1199 1257
1200 // check to see if we are updating a parameter for a particular or all of the prims
1201 protected void UpdateParameterObject(ref float loc, string parm, uint localID, float val)
1202 {
1203 List<uint> operateOn;
1204 lock (PhysObjects) operateOn = new List<uint>(PhysObjects.Keys);
1205 UpdateParameterSet(operateOn, ref loc, parm, localID, val);
1206 }
1207
1208 // update all the localIDs specified 1258 // update all the localIDs specified
1209 // If the local ID is APPLY_TO_NONE, just change the default value 1259 // If the local ID is APPLY_TO_NONE, just change the default value
1210 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs 1260 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
1211 // If the localID is a specific object, apply the parameter change to only that object 1261 // If the localID is a specific object, apply the parameter change to only that object
1212 protected void UpdateParameterSet(List<uint> lIDs, ref float defaultLoc, string parm, uint localID, float val) 1262 protected void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val)
1213 { 1263 {
1264 List<uint> objectIDs = new List<uint>();
1214 switch (localID) 1265 switch (localID)
1215 { 1266 {
1216 case PhysParameterEntry.APPLY_TO_NONE: 1267 case PhysParameterEntry.APPLY_TO_NONE:
1217 defaultLoc = val; // setting only the default value 1268 defaultLoc = val; // setting only the default value
1269 // This will cause a call into the physical world if some operation is specified (SetOnObject).
1270 objectIDs.Add(TERRAIN_ID);
1271 TaintedUpdateParameter(parm, objectIDs, val);
1218 break; 1272 break;
1219 case PhysParameterEntry.APPLY_TO_ALL: 1273 case PhysParameterEntry.APPLY_TO_ALL:
1220 defaultLoc = val; // setting ALL also sets the default value 1274 defaultLoc = val; // setting ALL also sets the default value
1221 List<uint> objectIDs = lIDs; 1275 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
1222 string xparm = parm.ToLower(); 1276 TaintedUpdateParameter(parm, objectIDs, val);
1223 float xval = val;
1224 TaintedObject("BSScene.UpdateParameterSet", delegate() {
1225 foreach (uint lID in objectIDs)
1226 {
1227 BulletSimAPI.UpdateParameter(WorldID, lID, xparm, xval);
1228 }
1229 });
1230 break; 1277 break;
1231 default: 1278 default:
1232 // setting only one localID 1279 // setting only one localID
1233 TaintedUpdateParameter(parm, localID, val); 1280 objectIDs.Add(localID);
1281 TaintedUpdateParameter(parm, objectIDs, val);
1234 break; 1282 break;
1235 } 1283 }
1236 } 1284 }
1237 1285
1238 // schedule the actual updating of the paramter to when the phys engine is not busy 1286 // schedule the actual updating of the paramter to when the phys engine is not busy
1239 protected void TaintedUpdateParameter(string parm, uint localID, float val) 1287 protected void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
1240 { 1288 {
1241 uint xlocalID = localID;
1242 string xparm = parm.ToLower();
1243 float xval = val; 1289 float xval = val;
1244 TaintedObject("BSScene.TaintedUpdateParameter", delegate() { 1290 List<uint> xlIDs = lIDs;
1245 BulletSimAPI.UpdateParameter(WorldID, xlocalID, xparm, xval); 1291 string xparm = parm;
1292 TaintedObject("BSScene.UpdateParameterSet", delegate() {
1293 ParameterDefn thisParam;
1294 if (TryGetParameter(xparm, out thisParam))
1295 {
1296 if (thisParam.onObject != null)
1297 {
1298 foreach (uint lID in xlIDs)
1299 {
1300 BSPhysObject theObject = null;
1301 PhysObjects.TryGetValue(lID, out theObject);
1302 thisParam.onObject(this, theObject, xval);
1303 }
1304 }
1305 }
1246 }); 1306 });
1247 } 1307 }
1248 1308
@@ -1270,6 +1330,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
1270 public void DetailLog(string msg, params Object[] args) 1330 public void DetailLog(string msg, params Object[] args)
1271 { 1331 {
1272 PhysicsLogging.Write(msg, args); 1332 PhysicsLogging.Write(msg, args);
1333 // Add the Flush() if debugging crashes to get all the messages written out.
1334 // PhysicsLogging.Flush();
1273 } 1335 }
1274 // used to fill in the LocalID when there isn't one 1336 // used to fill in the LocalID when there isn't one
1275 public const string DetailLogZero = "0000000000"; 1337 public const string DetailLogZero = "0000000000";
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
index 399a133..d3ba273 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
@@ -51,7 +51,7 @@ public class BSShapeCollection : IDisposable
51 } 51 }
52 52
53 // Description of a hull. 53 // Description of a hull.
54 // Meshes and hulls have the same shape hash key but we only need hulls for efficient physical objects 54 // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations.
55 private struct HullDesc 55 private struct HullDesc
56 { 56 {
57 public IntPtr ptr; 57 public IntPtr ptr;
@@ -59,17 +59,9 @@ public class BSShapeCollection : IDisposable
59 public DateTime lastReferenced; 59 public DateTime lastReferenced;
60 } 60 }
61 61
62 private struct BodyDesc 62 // The sharable set of meshes and hulls. Indexed by their shape hash.
63 { 63 private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
64 public IntPtr ptr; 64 private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
65 // Bodies are only used once so reference count is always either one or zero
66 public int referenceCount;
67 public DateTime lastReferenced;
68 }
69
70 private Dictionary<ulong, MeshDesc> Meshes = new Dictionary<ulong, MeshDesc>();
71 private Dictionary<ulong, HullDesc> Hulls = new Dictionary<ulong, HullDesc>();
72 private Dictionary<uint, BodyDesc> Bodies = new Dictionary<uint, BodyDesc>();
73 65
74 public BSShapeCollection(BSScene physScene) 66 public BSShapeCollection(BSScene physScene)
75 { 67 {
@@ -92,8 +84,12 @@ public class BSShapeCollection : IDisposable
92 // First checks the shape and updates that if necessary then makes 84 // First checks the shape and updates that if necessary then makes
93 // sure the body is of the right type. 85 // sure the body is of the right type.
94 // Return 'true' if either the body or the shape changed. 86 // Return 'true' if either the body or the shape changed.
87 // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
88 // the current shape or body is destroyed. This allows the caller to remove any
89 // higher level dependencies on the shape or body. Mostly used for LinkSets to
90 // remove the physical constraints before the body is destroyed.
95 // Called at taint-time!! 91 // Called at taint-time!!
96 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim, 92 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
97 ShapeData shapeData, PrimitiveBaseShape pbs, 93 ShapeData shapeData, PrimitiveBaseShape pbs,
98 ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) 94 ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
99 { 95 {
@@ -103,7 +99,8 @@ public class BSShapeCollection : IDisposable
103 lock (m_collectionActivityLock) 99 lock (m_collectionActivityLock)
104 { 100 {
105 // Do we have the correct geometry for this type of object? 101 // Do we have the correct geometry for this type of object?
106 // Updates prim.BSShape with information/pointers to requested shape 102 // Updates prim.BSShape with information/pointers to shape.
103 // CreateGeom returns 'true' of BSShape as changed to a new shape.
107 bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback); 104 bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback);
108 // If we had to select a new shape geometry for the object, 105 // If we had to select a new shape geometry for the object,
109 // rebuild the body around it. 106 // rebuild the body around it.
@@ -120,26 +117,24 @@ public class BSShapeCollection : IDisposable
120 117
121 // Track another user of a body 118 // Track another user of a body
122 // We presume the caller has allocated the body. 119 // We presume the caller has allocated the body.
123 // Bodies only have one user so the reference count is either 1 or 0. 120 // Bodies only have one user so the body is just put into the world if not already there.
124 public void ReferenceBody(BulletBody body, bool atTaintTime) 121 public void ReferenceBody(BulletBody body, bool inTaintTime)
125 { 122 {
126 lock (m_collectionActivityLock) 123 lock (m_collectionActivityLock)
127 { 124 {
128 BodyDesc bodyDesc; 125 DetailLog("{0},BSShapeCollection.ReferenceBody,newBody", body.ID, body);
129 if (Bodies.TryGetValue(body.ID, out bodyDesc)) 126 BSScene.TaintCallback createOperation = delegate()
130 { 127 {
131 bodyDesc.referenceCount++; 128 if (!BulletSimAPI.IsInWorld2(body.ptr))
132 DetailLog("{0},BSShapeCollection.ReferenceBody,existingBody,body={1},ref={2}", body.ID, body, bodyDesc.referenceCount); 129 {
133 } 130 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
131 DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
132 }
133 };
134 if (inTaintTime)
135 createOperation();
134 else 136 else
135 { 137 PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation);
136 // New entry
137 bodyDesc.ptr = body.ptr;
138 bodyDesc.referenceCount = 1;
139 DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,ref={1}", body.ID, body, bodyDesc.referenceCount);
140 }
141 bodyDesc.lastReferenced = System.DateTime.Now;
142 Bodies[body.ID] = bodyDesc;
143 } 138 }
144 } 139 }
145 140
@@ -152,42 +147,25 @@ public class BSShapeCollection : IDisposable
152 147
153 lock (m_collectionActivityLock) 148 lock (m_collectionActivityLock)
154 { 149 {
155 BodyDesc bodyDesc; 150 BSScene.TaintCallback removeOperation = delegate()
156 if (Bodies.TryGetValue(body.ID, out bodyDesc))
157 { 151 {
158 bodyDesc.referenceCount--; 152 DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}",
159 bodyDesc.lastReferenced = System.DateTime.Now; 153 body.ID, body.ptr.ToString("X"), inTaintTime);
160 Bodies[body.ID] = bodyDesc; 154 // If the caller needs to know the old body is going away, pass the event up.
161 DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount); 155 if (bodyCallback != null) bodyCallback(body);
162 156
163 // If body is no longer being used, free it -- bodies are never shared. 157 // It may have already been removed from the world in which case the next is a NOOP.
164 if (bodyDesc.referenceCount == 0) 158 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
165 { 159
166 Bodies.Remove(body.ID); 160 // Zero any reference to the shape so it is not freed when the body is deleted.
167 BSScene.TaintCallback removeOperation = delegate() 161 BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
168 { 162 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
169 DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}", 163 };
170 body.ID, body.ptr.ToString("X")); 164 // If already in taint-time, do the operations now. Otherwise queue for later.
171 // If the caller needs to know the old body is going away, pass the event up. 165 if (inTaintTime)
172 if (bodyCallback != null) bodyCallback(body); 166 removeOperation();
173
174 // Zero any reference to the shape so it is not freed when the body is deleted.
175 BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
176 // It may have already been removed from the world in which case the next is a NOOP.
177 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
178 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
179 };
180 // If already in taint-time, do the operations now. Otherwise queue for later.
181 if (inTaintTime)
182 removeOperation();
183 else
184 PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
185 }
186 }
187 else 167 else
188 { 168 PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
189 DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount);
190 }
191 } 169 }
192 } 170 }
193 171
@@ -208,7 +186,7 @@ public class BSShapeCollection : IDisposable
208 { 186 {
209 // There is an existing instance of this mesh. 187 // There is an existing instance of this mesh.
210 meshDesc.referenceCount++; 188 meshDesc.referenceCount++;
211 DetailLog("{0},BSShapeColliction.ReferenceShape,existingMesh,key={1},cnt={2}", 189 DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
212 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); 190 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
213 } 191 }
214 else 192 else
@@ -217,7 +195,7 @@ public class BSShapeCollection : IDisposable
217 meshDesc.ptr = shape.ptr; 195 meshDesc.ptr = shape.ptr;
218 // We keep a reference to the underlying IMesh data so a hull can be built 196 // We keep a reference to the underlying IMesh data so a hull can be built
219 meshDesc.referenceCount = 1; 197 meshDesc.referenceCount = 1;
220 DetailLog("{0},BSShapeColliction.ReferenceShape,newMesh,key={1},cnt={2}", 198 DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
221 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); 199 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
222 ret = true; 200 ret = true;
223 } 201 }
@@ -230,7 +208,7 @@ public class BSShapeCollection : IDisposable
230 { 208 {
231 // There is an existing instance of this hull. 209 // There is an existing instance of this hull.
232 hullDesc.referenceCount++; 210 hullDesc.referenceCount++;
233 DetailLog("{0},BSShapeColliction.ReferenceShape,existingHull,key={1},cnt={2}", 211 DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
234 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); 212 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
235 } 213 }
236 else 214 else
@@ -238,7 +216,7 @@ public class BSShapeCollection : IDisposable
238 // This is a new reference to a hull 216 // This is a new reference to a hull
239 hullDesc.ptr = shape.ptr; 217 hullDesc.ptr = shape.ptr;
240 hullDesc.referenceCount = 1; 218 hullDesc.referenceCount = 1;
241 DetailLog("{0},BSShapeColliction.ReferenceShape,newHull,key={1},cnt={2}", 219 DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
242 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); 220 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
243 ret = true; 221 ret = true;
244 222
@@ -256,37 +234,42 @@ public class BSShapeCollection : IDisposable
256 } 234 }
257 235
258 // Release the usage of a shape. 236 // Release the usage of a shape.
259 // The collisionObject is released since it is a copy of the real collision shape. 237 public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
260 public void DereferenceShape(BulletShape shape, bool atTaintTime, ShapeDestructionCallback shapeCallback)
261 { 238 {
262 if (shape.ptr == IntPtr.Zero) 239 if (shape.ptr == IntPtr.Zero)
263 return; 240 return;
264 241
265 BSScene.TaintCallback dereferenceOperation = delegate() 242 BSScene.TaintCallback dereferenceOperation = delegate()
266 { 243 {
267 switch (shape.type) 244 if (shape.ptr != IntPtr.Zero)
268 { 245 {
269 case ShapeData.PhysicsShapeType.SHAPE_HULL: 246 if (shape.isNativeShape)
270 DereferenceHull(shape, shapeCallback); 247 {
271 break;
272 case ShapeData.PhysicsShapeType.SHAPE_MESH:
273 DereferenceMesh(shape, shapeCallback);
274 break;
275 case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
276 break;
277 default:
278 // Native shapes are not tracked and are released immediately 248 // Native shapes are not tracked and are released immediately
279 if (shape.ptr != IntPtr.Zero & shape.isNativeShape) 249 DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
250 BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime);
251 if (shapeCallback != null) shapeCallback(shape);
252 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
253 }
254 else
255 {
256 switch (shape.type)
280 { 257 {
281 DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", 258 case ShapeData.PhysicsShapeType.SHAPE_HULL:
282 BSScene.DetailLogZero, shape.ptr.ToString("X"), atTaintTime); 259 DereferenceHull(shape, shapeCallback);
283 if (shapeCallback != null) shapeCallback(shape); 260 break;
284 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); 261 case ShapeData.PhysicsShapeType.SHAPE_MESH:
262 DereferenceMesh(shape, shapeCallback);
263 break;
264 case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
265 break;
266 default:
267 break;
285 } 268 }
286 break; 269 }
287 } 270 }
288 }; 271 };
289 if (atTaintTime) 272 if (inTaintTime)
290 { 273 {
291 lock (m_collectionActivityLock) 274 lock (m_collectionActivityLock)
292 { 275 {
@@ -336,19 +319,31 @@ public class BSShapeCollection : IDisposable
336 319
337 // Create the geometry information in Bullet for later use. 320 // Create the geometry information in Bullet for later use.
338 // The objects needs a hull if it's physical otherwise a mesh is enough. 321 // The objects needs a hull if it's physical otherwise a mesh is enough.
339 // No locking here because this is done when we know physics is not simulating. 322 // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
340 // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used. 323 // shared geometries will be used. If the parameters of the existing shape are the same
324 // as this request, the shape is not rebuilt.
325 // Info in prim.BSShape is updated to the new shape.
341 // Returns 'true' if the geometry was rebuilt. 326 // Returns 'true' if the geometry was rebuilt.
342 // Called at taint-time! 327 // Called at taint-time!
343 private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData, 328 private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeData shapeData,
344 PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) 329 PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback)
345 { 330 {
346 bool ret = false; 331 bool ret = false;
347 bool haveShape = false; 332 bool haveShape = false;
348 bool nativeShapePossible = true; 333 bool nativeShapePossible = true;
349 334
335 if (shapeData.Type == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
336 {
337 // an avatar capsule is close to a native shape (it is not shared)
338 ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_AVATAR,
339 ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback);
340 DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.BSShape);
341 haveShape = true;
342 }
350 // If the prim attributes are simple, this could be a simple Bullet native shape 343 // If the prim attributes are simple, this could be a simple Bullet native shape
351 if (nativeShapePossible 344 if (!haveShape
345 && pbs != null
346 && nativeShapePossible
352 && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim) 347 && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim)
353 || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 348 || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
354 && pbs.ProfileHollow == 0 349 && pbs.ProfileHollow == 0
@@ -358,7 +353,8 @@ public class BSShapeCollection : IDisposable
358 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 353 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
359 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) 354 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
360 { 355 {
361 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) 356 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
357 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
362 { 358 {
363 haveShape = true; 359 haveShape = true;
364 if (forceRebuild 360 if (forceRebuild
@@ -372,7 +368,7 @@ public class BSShapeCollection : IDisposable
372 prim.LocalID, forceRebuild, prim.BSShape); 368 prim.LocalID, forceRebuild, prim.BSShape);
373 } 369 }
374 } 370 }
375 else 371 if (pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
376 { 372 {
377 haveShape = true; 373 haveShape = true;
378 if (forceRebuild 374 if (forceRebuild
@@ -390,9 +386,9 @@ public class BSShapeCollection : IDisposable
390 // If a simple shape is not happening, create a mesh and possibly a hull. 386 // If a simple shape is not happening, create a mesh and possibly a hull.
391 // Note that if it's a native shape, the check for physical/non-physical is not 387 // Note that if it's a native shape, the check for physical/non-physical is not
392 // made. Native shapes are best used in either case. 388 // made. Native shapes are best used in either case.
393 if (!haveShape) 389 if (!haveShape && pbs != null)
394 { 390 {
395 if (prim.IsPhysical) 391 if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects)
396 { 392 {
397 // Update prim.BSShape to reference a hull of this shape. 393 // Update prim.BSShape to reference a hull of this shape.
398 ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback); 394 ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback);
@@ -409,12 +405,12 @@ public class BSShapeCollection : IDisposable
409 return ret; 405 return ret;
410 } 406 }
411 407
412 // Creates a native shape and assignes it to prim.BSShape 408 // Creates a native shape and assignes it to prim.BSShape.
413 private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData, 409 // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
410 private bool GetReferenceToNativeShape(BSPhysObject prim, ShapeData shapeData,
414 ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, 411 ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey,
415 ShapeDestructionCallback shapeCallback) 412 ShapeDestructionCallback shapeCallback)
416 { 413 {
417 BulletShape newShape;
418 414
419 shapeData.Type = shapeType; 415 shapeData.Type = shapeType;
420 // Bullet native objects are scaled by the Bullet engine so pass the size in 416 // Bullet native objects are scaled by the Bullet engine so pass the size in
@@ -424,29 +420,48 @@ public class BSShapeCollection : IDisposable
424 // release any previous shape 420 // release any previous shape
425 DereferenceShape(prim.BSShape, true, shapeCallback); 421 DereferenceShape(prim.BSShape, true, shapeCallback);
426 422
427 // Native shapes are always built independently. 423 BulletShape newShape = BuildPhysicalNativeShape(shapeType, shapeData, shapeKey);
428 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
429 newShape.shapeKey = (ulong)shapeKey;
430 newShape.isNativeShape = true;
431 424
432 // Don't need to do a 'ReferenceShape()' here because native shapes are not tracked. 425 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
433 // DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1}", shapeData.ID, newShape); 426 DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
427 shapeData.ID, newShape, shapeData.Scale);
434 428
435 prim.BSShape = newShape; 429 prim.BSShape = newShape;
436 return true; 430 return true;
437 } 431 }
438 432
433 private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType,
434 ShapeData shapeData, ShapeData.FixedShapeKey shapeKey)
435 {
436 BulletShape newShape;
437
438 if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
439 {
440 newShape = new BulletShape(
441 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1.0f, 1.0f, shapeData.Scale),
442 shapeType);
443 }
444 else
445 {
446 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
447 }
448 newShape.shapeKey = (System.UInt64)shapeKey;
449 newShape.isNativeShape = true;
450
451 return newShape;
452 }
453
439 // Builds a mesh shape in the physical world and updates prim.BSShape. 454 // Builds a mesh shape in the physical world and updates prim.BSShape.
440 // Dereferences previous shape in BSShape and adds a reference for this new shape. 455 // Dereferences previous shape in BSShape and adds a reference for this new shape.
441 // Returns 'true' of a mesh was actually built. Otherwise . 456 // Returns 'true' of a mesh was actually built. Otherwise .
442 // Called at taint-time! 457 // Called at taint-time!
443 private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs, 458 private bool GetReferenceToMesh(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs,
444 ShapeDestructionCallback shapeCallback) 459 ShapeDestructionCallback shapeCallback)
445 { 460 {
446 BulletShape newShape = new BulletShape(IntPtr.Zero); 461 BulletShape newShape = new BulletShape(IntPtr.Zero);
447 462
448 float lod; 463 float lod;
449 ulong newMeshKey = ComputeShapeKey(shapeData, pbs, out lod); 464 System.UInt64 newMeshKey = ComputeShapeKey(shapeData, pbs, out lod);
450 465
451 // if this new shape is the same as last time, don't recreate the mesh 466 // if this new shape is the same as last time, don't recreate the mesh
452 if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH) 467 if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH)
@@ -459,6 +474,8 @@ public class BSShapeCollection : IDisposable
459 DereferenceShape(prim.BSShape, true, shapeCallback); 474 DereferenceShape(prim.BSShape, true, shapeCallback);
460 475
461 newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod); 476 newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod);
477 // Take evasive action if the mesh was not constructed.
478 newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
462 479
463 ReferenceShape(newShape); 480 ReferenceShape(newShape);
464 481
@@ -469,10 +486,10 @@ public class BSShapeCollection : IDisposable
469 return true; // 'true' means a new shape has been added to this prim 486 return true; // 'true' means a new shape has been added to this prim
470 } 487 }
471 488
472 private BulletShape CreatePhysicalMesh(string objName, ulong newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) 489 private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
473 { 490 {
474 IMesh meshData = null; 491 IMesh meshData = null;
475 IntPtr meshPtr; 492 IntPtr meshPtr = IntPtr.Zero;
476 MeshDesc meshDesc; 493 MeshDesc meshDesc;
477 if (Meshes.TryGetValue(newMeshKey, out meshDesc)) 494 if (Meshes.TryGetValue(newMeshKey, out meshDesc))
478 { 495 {
@@ -484,23 +501,26 @@ public class BSShapeCollection : IDisposable
484 // Pass false for physicalness as this creates some sort of bounding box which we don't need 501 // Pass false for physicalness as this creates some sort of bounding box which we don't need
485 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); 502 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
486 503
487 int[] indices = meshData.getIndexListAsInt(); 504 if (meshData != null)
488 List<OMV.Vector3> vertices = meshData.getVertexList();
489
490 float[] verticesAsFloats = new float[vertices.Count * 3];
491 int vi = 0;
492 foreach (OMV.Vector3 vv in vertices)
493 { 505 {
494 verticesAsFloats[vi++] = vv.X; 506 int[] indices = meshData.getIndexListAsInt();
495 verticesAsFloats[vi++] = vv.Y; 507 List<OMV.Vector3> vertices = meshData.getVertexList();
496 verticesAsFloats[vi++] = vv.Z; 508
497 } 509 float[] verticesAsFloats = new float[vertices.Count * 3];
510 int vi = 0;
511 foreach (OMV.Vector3 vv in vertices)
512 {
513 verticesAsFloats[vi++] = vv.X;
514 verticesAsFloats[vi++] = vv.Y;
515 verticesAsFloats[vi++] = vv.Z;
516 }
498 517
499 // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", 518 // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
500 // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count); 519 // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
501 520
502 meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, 521 meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
503 indices.GetLength(0), indices, vertices.Count, verticesAsFloats); 522 indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
523 }
504 } 524 }
505 BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); 525 BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH);
506 newShape.shapeKey = newMeshKey; 526 newShape.shapeKey = newMeshKey;
@@ -510,13 +530,13 @@ public class BSShapeCollection : IDisposable
510 530
511 // See that hull shape exists in the physical world and update prim.BSShape. 531 // See that hull shape exists in the physical world and update prim.BSShape.
512 // We could be creating the hull because scale changed or whatever. 532 // We could be creating the hull because scale changed or whatever.
513 private bool GetReferenceToHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs, 533 private bool GetReferenceToHull(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs,
514 ShapeDestructionCallback shapeCallback) 534 ShapeDestructionCallback shapeCallback)
515 { 535 {
516 BulletShape newShape; 536 BulletShape newShape;
517 537
518 float lod; 538 float lod;
519 ulong newHullKey = ComputeShapeKey(shapeData, pbs, out lod); 539 System.UInt64 newHullKey = ComputeShapeKey(shapeData, pbs, out lod);
520 540
521 // if the hull hasn't changed, don't rebuild it 541 // if the hull hasn't changed, don't rebuild it
522 if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) 542 if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL)
@@ -525,10 +545,11 @@ public class BSShapeCollection : IDisposable
525 DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}", 545 DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}",
526 prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X")); 546 prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
527 547
528 // Remove usage of the previous shape. Also removes reference to underlying mesh if it is a hull. 548 // Remove usage of the previous shape.
529 DereferenceShape(prim.BSShape, true, shapeCallback); 549 DereferenceShape(prim.BSShape, true, shapeCallback);
530 550
531 newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod); 551 newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod);
552 newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
532 553
533 ReferenceShape(newShape); 554 ReferenceShape(newShape);
534 555
@@ -539,10 +560,10 @@ public class BSShapeCollection : IDisposable
539 } 560 }
540 561
541 List<ConvexResult> m_hulls; 562 List<ConvexResult> m_hulls;
542 private BulletShape CreatePhysicalHull(string objName, ulong newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) 563 private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
543 { 564 {
544 565
545 IntPtr hullPtr; 566 IntPtr hullPtr = IntPtr.Zero;
546 HullDesc hullDesc; 567 HullDesc hullDesc;
547 if (Hulls.TryGetValue(newHullKey, out hullDesc)) 568 if (Hulls.TryGetValue(newHullKey, out hullDesc))
548 { 569 {
@@ -554,86 +575,89 @@ public class BSShapeCollection : IDisposable
554 // Build a new hull in the physical world 575 // Build a new hull in the physical world
555 // Pass false for physicalness as this creates some sort of bounding box which we don't need 576 // Pass false for physicalness as this creates some sort of bounding box which we don't need
556 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); 577 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
557 578 if (meshData != null)
558 int[] indices = meshData.getIndexListAsInt();
559 List<OMV.Vector3> vertices = meshData.getVertexList();
560
561 //format conversion from IMesh format to DecompDesc format
562 List<int> convIndices = new List<int>();
563 List<float3> convVertices = new List<float3>();
564 for (int ii = 0; ii < indices.GetLength(0); ii++)
565 { 579 {
566 convIndices.Add(indices[ii]);
567 }
568 foreach (OMV.Vector3 vv in vertices)
569 {
570 convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
571 }
572 580
573 // setup and do convex hull conversion 581 int[] indices = meshData.getIndexListAsInt();
574 m_hulls = new List<ConvexResult>(); 582 List<OMV.Vector3> vertices = meshData.getVertexList();
575 DecompDesc dcomp = new DecompDesc();
576 dcomp.mIndices = convIndices;
577 dcomp.mVertices = convVertices;
578 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
579 // create the hull into the _hulls variable
580 convexBuilder.process(dcomp);
581
582 // Convert the vertices and indices for passing to unmanaged.
583 // The hull information is passed as a large floating point array.
584 // The format is:
585 // convHulls[0] = number of hulls
586 // convHulls[1] = number of vertices in first hull
587 // convHulls[2] = hull centroid X coordinate
588 // convHulls[3] = hull centroid Y coordinate
589 // convHulls[4] = hull centroid Z coordinate
590 // convHulls[5] = first hull vertex X
591 // convHulls[6] = first hull vertex Y
592 // convHulls[7] = first hull vertex Z
593 // convHulls[8] = second hull vertex X
594 // ...
595 // convHulls[n] = number of vertices in second hull
596 // convHulls[n+1] = second hull centroid X coordinate
597 // ...
598 //
599 // TODO: is is very inefficient. Someday change the convex hull generator to return
600 // data structures that do not need to be converted in order to pass to Bullet.
601 // And maybe put the values directly into pinned memory rather than marshaling.
602 int hullCount = m_hulls.Count;
603 int totalVertices = 1; // include one for the count of the hulls
604 foreach (ConvexResult cr in m_hulls)
605 {
606 totalVertices += 4; // add four for the vertex count and centroid
607 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
608 }
609 float[] convHulls = new float[totalVertices];
610 583
611 convHulls[0] = (float)hullCount; 584 //format conversion from IMesh format to DecompDesc format
612 int jj = 1; 585 List<int> convIndices = new List<int>();
613 foreach (ConvexResult cr in m_hulls) 586 List<float3> convVertices = new List<float3>();
614 { 587 for (int ii = 0; ii < indices.GetLength(0); ii++)
615 // copy vertices for index access 588 {
616 float3[] verts = new float3[cr.HullVertices.Count]; 589 convIndices.Add(indices[ii]);
617 int kk = 0; 590 }
618 foreach (float3 ff in cr.HullVertices) 591 foreach (OMV.Vector3 vv in vertices)
592 {
593 convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
594 }
595
596 // setup and do convex hull conversion
597 m_hulls = new List<ConvexResult>();
598 DecompDesc dcomp = new DecompDesc();
599 dcomp.mIndices = convIndices;
600 dcomp.mVertices = convVertices;
601 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
602 // create the hull into the _hulls variable
603 convexBuilder.process(dcomp);
604
605 // Convert the vertices and indices for passing to unmanaged.
606 // The hull information is passed as a large floating point array.
607 // The format is:
608 // convHulls[0] = number of hulls
609 // convHulls[1] = number of vertices in first hull
610 // convHulls[2] = hull centroid X coordinate
611 // convHulls[3] = hull centroid Y coordinate
612 // convHulls[4] = hull centroid Z coordinate
613 // convHulls[5] = first hull vertex X
614 // convHulls[6] = first hull vertex Y
615 // convHulls[7] = first hull vertex Z
616 // convHulls[8] = second hull vertex X
617 // ...
618 // convHulls[n] = number of vertices in second hull
619 // convHulls[n+1] = second hull centroid X coordinate
620 // ...
621 //
622 // TODO: is is very inefficient. Someday change the convex hull generator to return
623 // data structures that do not need to be converted in order to pass to Bullet.
624 // And maybe put the values directly into pinned memory rather than marshaling.
625 int hullCount = m_hulls.Count;
626 int totalVertices = 1; // include one for the count of the hulls
627 foreach (ConvexResult cr in m_hulls)
619 { 628 {
620 verts[kk++] = ff; 629 totalVertices += 4; // add four for the vertex count and centroid
630 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
621 } 631 }
632 float[] convHulls = new float[totalVertices];
622 633
623 // add to the array one hull's worth of data 634 convHulls[0] = (float)hullCount;
624 convHulls[jj++] = cr.HullIndices.Count; 635 int jj = 1;
625 convHulls[jj++] = 0f; // centroid x,y,z 636 foreach (ConvexResult cr in m_hulls)
626 convHulls[jj++] = 0f;
627 convHulls[jj++] = 0f;
628 foreach (int ind in cr.HullIndices)
629 { 637 {
630 convHulls[jj++] = verts[ind].x; 638 // copy vertices for index access
631 convHulls[jj++] = verts[ind].y; 639 float3[] verts = new float3[cr.HullVertices.Count];
632 convHulls[jj++] = verts[ind].z; 640 int kk = 0;
641 foreach (float3 ff in cr.HullVertices)
642 {
643 verts[kk++] = ff;
644 }
645
646 // add to the array one hull's worth of data
647 convHulls[jj++] = cr.HullIndices.Count;
648 convHulls[jj++] = 0f; // centroid x,y,z
649 convHulls[jj++] = 0f;
650 convHulls[jj++] = 0f;
651 foreach (int ind in cr.HullIndices)
652 {
653 convHulls[jj++] = verts[ind].x;
654 convHulls[jj++] = verts[ind].y;
655 convHulls[jj++] = verts[ind].z;
656 }
633 } 657 }
658 // create the hull data structure in Bullet
659 hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
634 } 660 }
635 // create the hull data structure in Bullet
636 hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
637 } 661 }
638 662
639 BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); 663 BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL);
@@ -652,32 +676,77 @@ public class BSShapeCollection : IDisposable
652 676
653 // Create a hash of all the shape parameters to be used as a key 677 // Create a hash of all the shape parameters to be used as a key
654 // for this particular shape. 678 // for this particular shape.
655 private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod) 679 private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod)
656 { 680 {
657 // level of detail based on size and type of the object 681 // level of detail based on size and type of the object
658 float lod = PhysicsScene.MeshLOD; 682 float lod = PhysicsScene.MeshLOD;
659 if (pbs.SculptEntry) 683 if (pbs.SculptEntry)
660 lod = PhysicsScene.SculptLOD; 684 lod = PhysicsScene.SculptLOD;
661 685
686 // Mega prims usually get more detail because one can interact with shape approximations at this size.
662 float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z)); 687 float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z));
663 if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) 688 if (maxAxis > PhysicsScene.MeshMegaPrimThreshold)
664 lod = PhysicsScene.MeshMegaPrimLOD; 689 lod = PhysicsScene.MeshMegaPrimLOD;
665 690
666 retLod = lod; 691 retLod = lod;
667 return (ulong)pbs.GetMeshKey(shapeData.Size, lod); 692 return pbs.GetMeshKey(shapeData.Size, lod);
668 } 693 }
669 // For those who don't want the LOD 694 // For those who don't want the LOD
670 private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs) 695 private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs)
671 { 696 {
672 float lod; 697 float lod;
673 return ComputeShapeKey(shapeData, pbs, out lod); 698 return ComputeShapeKey(shapeData, pbs, out lod);
674 } 699 }
675 700
701 private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs)
702 {
703 // If the shape was successfully created, nothing more to do
704 if (newShape.ptr != IntPtr.Zero)
705 return newShape;
706
707 // The most common reason for failure is that an underlying asset is not available
708
709 // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
710 if (pbs.SculptEntry && !prim.LastAssetBuildFailed && pbs.SculptTexture != OMV.UUID.Zero)
711 {
712 prim.LastAssetBuildFailed = true;
713 BSPhysObject xprim = prim;
714 Util.FireAndForget(delegate
715 {
716 RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
717 if (assetProvider != null)
718 {
719 BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
720 assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
721 {
722 if (!yprim.BaseShape.SculptEntry)
723 return;
724 if (yprim.BaseShape.SculptTexture.ToString() != asset.ID)
725 return;
726
727 yprim.BaseShape.SculptData = new byte[asset.Data.Length];
728 asset.Data.CopyTo(yprim.BaseShape.SculptData, 0);
729 // This will cause the prim to see that the filler shape is not the right
730 // one and try again to build the object.
731 yprim.ForceBodyShapeRebuild(false);
732
733 });
734 }
735 });
736 }
737
738 // While we figure out the real problem, stick a simple native shape on the object.
739 BulletShape fillinShape =
740 BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_SPHERE, shapeData, ShapeData.FixedShapeKey.KEY_SPHERE);
741
742 return fillinShape;
743 }
744
676 // Create a body object in Bullet. 745 // Create a body object in Bullet.
677 // Updates prim.BSBody with the information about the new body if one is created. 746 // Updates prim.BSBody with the information about the new body if one is created.
678 // Returns 'true' if an object was actually created. 747 // Returns 'true' if an object was actually created.
679 // Called at taint-time. 748 // Called at taint-time.
680 private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape, 749 private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape,
681 ShapeData shapeData, BodyDestructionCallback bodyCallback) 750 ShapeData shapeData, BodyDestructionCallback bodyCallback)
682 { 751 {
683 bool ret = false; 752 bool ret = false;
@@ -701,6 +770,7 @@ public class BSShapeCollection : IDisposable
701 770
702 if (mustRebuild || forceRebuild) 771 if (mustRebuild || forceRebuild)
703 { 772 {
773 // Free any old body
704 DereferenceBody(prim.BSBody, true, bodyCallback); 774 DereferenceBody(prim.BSBody, true, bodyCallback);
705 775
706 BulletBody aBody; 776 BulletBody aBody;
@@ -709,13 +779,13 @@ public class BSShapeCollection : IDisposable
709 { 779 {
710 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, 780 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
711 shapeData.ID, shapeData.Position, shapeData.Rotation); 781 shapeData.ID, shapeData.Position, shapeData.Rotation);
712 // DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); 782 DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
713 } 783 }
714 else 784 else
715 { 785 {
716 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, 786 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
717 shapeData.ID, shapeData.Position, shapeData.Rotation); 787 shapeData.ID, shapeData.Position, shapeData.Rotation);
718 // DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); 788 DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
719 } 789 }
720 aBody = new BulletBody(shapeData.ID, bodyPtr); 790 aBody = new BulletBody(shapeData.ID, bodyPtr);
721 791
@@ -731,7 +801,8 @@ public class BSShapeCollection : IDisposable
731 801
732 private void DetailLog(string msg, params Object[] args) 802 private void DetailLog(string msg, params Object[] args)
733 { 803 {
734 PhysicsScene.PhysicsLogging.Write(msg, args); 804 if (PhysicsScene.PhysicsLogging.Enabled)
805 PhysicsScene.DetailLog(msg, args);
735 } 806 }
736} 807}
737} 808}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
index 70aa429..4106534 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
@@ -114,6 +114,9 @@ public class BSTerrainManager
114 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, 114 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
115 Vector3.Zero, Quaternion.Identity)); 115 Vector3.Zero, Quaternion.Identity));
116 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr); 116 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr);
117 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
118 // Ground plane does not move
119 BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
117 // Everything collides with the ground plane. 120 // Everything collides with the ground plane.
118 BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, 121 BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr,
119 (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); 122 (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask);
@@ -201,10 +204,10 @@ public class BSTerrainManager
201 // The 'doNow' boolean says whether to do all the unmanaged activities right now (like when 204 // The 'doNow' boolean says whether to do all the unmanaged activities right now (like when
202 // calling this routine from initialization or taint-time routines) or whether to delay 205 // calling this routine from initialization or taint-time routines) or whether to delay
203 // all the unmanaged activities to taint-time. 206 // all the unmanaged activities to taint-time.
204 private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool atTaintTime) 207 private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
205 { 208 {
206 DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},atTaintTime={3}", 209 DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
207 BSScene.DetailLogZero, minCoords, maxCoords, atTaintTime); 210 BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
208 211
209 float minZ = float.MaxValue; 212 float minZ = float.MaxValue;
210 float maxZ = float.MinValue; 213 float maxZ = float.MinValue;
@@ -296,16 +299,16 @@ public class BSTerrainManager
296 mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID, 299 mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID,
297 mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN); 300 mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
298 301
302 // Create the terrain shape from the mapInfo
303 mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
304 ShapeData.PhysicsShapeType.SHAPE_TERRAIN);
305
299 // The terrain object initial position is at the center of the object 306 // The terrain object initial position is at the center of the object
300 Vector3 centerPos; 307 Vector3 centerPos;
301 centerPos.X = minCoords.X + (mapInfo.sizeX / 2f); 308 centerPos.X = minCoords.X + (mapInfo.sizeX / 2f);
302 centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f); 309 centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f);
303 centerPos.Z = minZ + ((maxZ - minZ) / 2f); 310 centerPos.Z = minZ + ((maxZ - minZ) / 2f);
304 311
305 // Create the terrain shape from the mapInfo
306 mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
307 ShapeData.PhysicsShapeType.SHAPE_TERRAIN);
308
309 mapInfo.terrainBody = new BulletBody(mapInfo.ID, 312 mapInfo.terrainBody = new BulletBody(mapInfo.ID,
310 BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr, 313 BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr,
311 id, centerPos, Quaternion.Identity)); 314 id, centerPos, Quaternion.Identity));
@@ -320,9 +323,6 @@ public class BSTerrainManager
320 BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution); 323 BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
321 BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT); 324 BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
322 325
323 BulletSimAPI.SetMassProps2(mapInfo.terrainBody.ptr, 0f, Vector3.Zero);
324 BulletSimAPI.UpdateInertiaTensor2(mapInfo.terrainBody.ptr);
325
326 // Return the new terrain to the world of physical objects 326 // Return the new terrain to the world of physical objects
327 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr); 327 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
328 328
@@ -335,14 +335,15 @@ public class BSTerrainManager
335 335
336 // Make sure the new shape is processed. 336 // Make sure the new shape is processed.
337 // BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true); 337 // BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true);
338 BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION); 338 BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING);
339 // BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
339 340
340 m_terrainModified = true; 341 m_terrainModified = true;
341 }; 342 };
342 343
343 // There is the option to do the changes now (we're already in 'taint time'), or 344 // There is the option to do the changes now (we're already in 'taint time'), or
344 // to do the Bullet operations later. 345 // to do the Bullet operations later.
345 if (atTaintTime) 346 if (inTaintTime)
346 rebuildOperation(); 347 rebuildOperation();
347 else 348 else
348 PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation); 349 PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation);
@@ -381,7 +382,7 @@ public class BSTerrainManager
381 }; 382 };
382 383
383 // If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time. 384 // If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time.
384 if (atTaintTime) 385 if (inTaintTime)
385 createOperation(); 386 createOperation();
386 else 387 else
387 PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation); 388 PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
index a43880d..5ffd591 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
@@ -101,9 +101,8 @@ public struct BulletShape
101 } 101 }
102 public IntPtr ptr; 102 public IntPtr ptr;
103 public ShapeData.PhysicsShapeType type; 103 public ShapeData.PhysicsShapeType type;
104 public ulong shapeKey; 104 public System.UInt64 shapeKey;
105 public bool isNativeShape; 105 public bool isNativeShape;
106 // Hulls have an underlying mesh. A pointer to it is hidden here.
107 public override string ToString() 106 public override string ToString()
108 { 107 {
109 StringBuilder buff = new StringBuilder(); 108 StringBuilder buff = new StringBuilder();
@@ -192,8 +191,9 @@ public struct ShapeData
192 SHAPE_SPHERE = 5, 191 SHAPE_SPHERE = 5,
193 SHAPE_MESH = 6, 192 SHAPE_MESH = 6,
194 SHAPE_HULL = 7, 193 SHAPE_HULL = 7,
195 SHAPE_GROUNDPLANE = 8, 194 // following defined by BulletSim
196 SHAPE_TERRAIN = 9, 195 SHAPE_GROUNDPLANE = 20,
196 SHAPE_TERRAIN = 21,
197 }; 197 };
198 public uint ID; 198 public uint ID;
199 public PhysicsShapeType Type; 199 public PhysicsShapeType Type;
@@ -223,6 +223,7 @@ public struct ShapeData
223 KEY_SPHERE = 2, 223 KEY_SPHERE = 2,
224 KEY_CONE = 3, 224 KEY_CONE = 3,
225 KEY_CYLINDER = 4, 225 KEY_CYLINDER = 4,
226 KEY_CAPSULE = 5,
226 } 227 }
227} 228}
228[StructLayout(LayoutKind.Sequential)] 229[StructLayout(LayoutKind.Sequential)]
@@ -282,6 +283,7 @@ public struct ConfigurationParameters
282 public float terrainHitFraction; 283 public float terrainHitFraction;
283 public float terrainRestitution; 284 public float terrainRestitution;
284 public float avatarFriction; 285 public float avatarFriction;
286 public float avatarStandingFriction;
285 public float avatarDensity; 287 public float avatarDensity;
286 public float avatarRestitution; 288 public float avatarRestitution;
287 public float avatarCapsuleRadius; 289 public float avatarCapsuleRadius;
@@ -305,6 +307,8 @@ public struct ConfigurationParameters
305 public float linkConstraintCFM; 307 public float linkConstraintCFM;
306 public float linkConstraintSolverIterations; 308 public float linkConstraintSolverIterations;
307 309
310 public float physicsLoggingFrames;
311
308 public const float numericTrue = 1f; 312 public const float numericTrue = 1f;
309 public const float numericFalse = 0f; 313 public const float numericFalse = 0f;
310} 314}
@@ -386,7 +390,7 @@ public enum CollisionFilterGroups : uint
386 VolumeDetectMask = ~BSensorTrigger, 390 VolumeDetectMask = ~BSensorTrigger,
387 TerrainFilter = BTerrainFilter, 391 TerrainFilter = BTerrainFilter,
388 TerrainMask = BAllFilter & ~BStaticFilter, 392 TerrainMask = BAllFilter & ~BStaticFilter,
389 GroundPlaneFilter = BAllFilter, 393 GroundPlaneFilter = BGroundPlaneFilter,
390 GroundPlaneMask = BAllFilter 394 GroundPlaneMask = BAllFilter
391 395
392}; 396};
@@ -426,6 +430,7 @@ public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg
426[return: MarshalAs(UnmanagedType.LPStr)] 430[return: MarshalAs(UnmanagedType.LPStr)]
427public static extern string GetVersion(); 431public static extern string GetVersion();
428 432
433/* Remove the linkage to the old api methods
429[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 434[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
430public static extern uint Initialize(Vector3 maxPosition, IntPtr parms, 435public static extern uint Initialize(Vector3 maxPosition, IntPtr parms,
431 int maxCollisions, IntPtr collisionArray, 436 int maxCollisions, IntPtr collisionArray,
@@ -529,7 +534,7 @@ public static extern Vector3 RecoverFromPenetration(uint worldID, uint id);
529// =============================================================================== 534// ===============================================================================
530[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 535[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
531public static extern void DumpBulletStatistics(); 536public static extern void DumpBulletStatistics();
532 537*/
533// Log a debug message 538// Log a debug message
534[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 539[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
535public static extern void SetDebugLogCallback(DebugLogCallback callback); 540public static extern void SetDebugLogCallback(DebugLogCallback callback);
@@ -560,7 +565,8 @@ public static extern IntPtr GetBodyHandle2(IntPtr world, uint id);
560[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 565[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
561public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms, 566public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms,
562 int maxCollisions, IntPtr collisionArray, 567 int maxCollisions, IntPtr collisionArray,
563 int maxUpdates, IntPtr updateArray); 568 int maxUpdates, IntPtr updateArray,
569 DebugLogCallback logRoutine);
564 570
565[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 571[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
566public static extern bool UpdateParameter2(IntPtr world, uint localID, String parm, float value); 572public static extern bool UpdateParameter2(IntPtr world, uint localID, String parm, float value);
@@ -602,6 +608,9 @@ public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData)
602public static extern bool IsNativeShape2(IntPtr shape); 608public static extern bool IsNativeShape2(IntPtr shape);
603 609
604[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 610[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
611public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale);
612
613[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
605public static extern IntPtr CreateCompoundShape2(IntPtr sim); 614public static extern IntPtr CreateCompoundShape2(IntPtr sim);
606 615
607[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 616[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
@@ -1037,18 +1046,6 @@ public static extern IntPtr GetConstraintRef2(IntPtr obj, int index);
1037public static extern int GetNumConstraintRefs2(IntPtr obj); 1046public static extern int GetNumConstraintRefs2(IntPtr obj);
1038 1047
1039[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 1048[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1040public static extern Vector3 GetDeltaLinearVelocity2(IntPtr obj);
1041
1042[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1043public static extern Vector3 GetDeltaAngularVelocity2(IntPtr obj);
1044
1045[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1046public static extern Vector3 GetPushVelocity2(IntPtr obj);
1047
1048[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1049public static extern Vector3 GetTurnVelocity2(IntPtr obj);
1050
1051[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1052public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask); 1049public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask);
1053 1050
1054// ===================================================================================== 1051// =====================================================================================
@@ -1108,6 +1105,15 @@ public static extern float GetMargin2(IntPtr shape);
1108public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject); 1105public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject);
1109 1106
1110[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 1107[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1108public static extern void DumpCollisionShape2(IntPtr sim, IntPtr collisionShape);
1109
1110[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1111public static extern void DumpConstraint2(IntPtr sim, IntPtr constrain);
1112
1113[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1114public static extern void DumpAllInfo2(IntPtr sim);
1115
1116[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1111public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo); 1117public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);
1112 1118
1113[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 1119[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs
index 6a0558a..488900e 100644
--- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs
+++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs
@@ -76,7 +76,7 @@ namespace OpenSim.Region.Physics.Manager
76 get { return new NullPhysicsScene(); } 76 get { return new NullPhysicsScene(); }
77 } 77 }
78 78
79 public RequestAssetDelegate RequestAssetMethod { private get; set; } 79 public RequestAssetDelegate RequestAssetMethod { get; set; }
80 80
81 public virtual void TriggerPhysicsBasedRestart() 81 public virtual void TriggerPhysicsBasedRestart()
82 { 82 {
diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
index e7b3b2b..2548648 100644
--- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
+++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
@@ -67,6 +67,14 @@ namespace OpenSim.Region.Physics.OdePlugin
67 private int m_expectedCollisionContacts = 0; 67 private int m_expectedCollisionContacts = 0;
68 68
69 /// <summary> 69 /// <summary>
70 /// Gets collide bits so that we can still perform land collisions if a mesh fails to load.
71 /// </summary>
72 private int BadMeshAssetCollideBits
73 {
74 get { return m_isphysical ? (int)CollisionCategories.Land : 0; }
75 }
76
77 /// <summary>
70 /// Is this prim subject to physics? Even if not, it's still solid for collision purposes. 78 /// Is this prim subject to physics? Even if not, it's still solid for collision purposes.
71 /// </summary> 79 /// </summary>
72 public override bool IsPhysical 80 public override bool IsPhysical
@@ -100,6 +108,9 @@ namespace OpenSim.Region.Physics.OdePlugin
100 private Vector3 m_taintAngularLock = Vector3.One; 108 private Vector3 m_taintAngularLock = Vector3.One;
101 private IntPtr Amotor = IntPtr.Zero; 109 private IntPtr Amotor = IntPtr.Zero;
102 110
111 private object m_assetsLock = new object();
112 private bool m_assetFailed = false;
113
103 private Vector3 m_PIDTarget; 114 private Vector3 m_PIDTarget;
104 private float m_PIDTau; 115 private float m_PIDTau;
105 private float PID_D = 35f; 116 private float PID_D = 35f;
@@ -282,6 +293,7 @@ namespace OpenSim.Region.Physics.OdePlugin
282 } 293 }
283 294
284 m_taintadd = true; 295 m_taintadd = true;
296 m_assetFailed = false;
285 _parent_scene.AddPhysicsActorTaint(this); 297 _parent_scene.AddPhysicsActorTaint(this);
286 } 298 }
287 299
@@ -337,8 +349,16 @@ namespace OpenSim.Region.Physics.OdePlugin
337 prim_geom = geom; 349 prim_geom = geom;
338//Console.WriteLine("SetGeom to " + prim_geom + " for " + Name); 350//Console.WriteLine("SetGeom to " + prim_geom + " for " + Name);
339 351
340 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); 352 if (m_assetFailed)
341 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); 353 {
354 d.GeomSetCategoryBits(prim_geom, 0);
355 d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
356 }
357 else
358 {
359 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
360 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
361 }
342 362
343 _parent_scene.geom_name_map[prim_geom] = Name; 363 _parent_scene.geom_name_map[prim_geom] = Name;
344 _parent_scene.actor_name_map[prim_geom] = this; 364 _parent_scene.actor_name_map[prim_geom] = this;
@@ -401,8 +421,17 @@ namespace OpenSim.Region.Physics.OdePlugin
401 myrot.W = _orientation.W; 421 myrot.W = _orientation.W;
402 d.BodySetQuaternion(Body, ref myrot); 422 d.BodySetQuaternion(Body, ref myrot);
403 d.GeomSetBody(prim_geom, Body); 423 d.GeomSetBody(prim_geom, Body);
404 m_collisionCategories |= CollisionCategories.Body; 424
405 m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); 425 if (m_assetFailed)
426 {
427 d.GeomSetCategoryBits(prim_geom, 0);
428 d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
429 }
430 else
431 {
432 m_collisionCategories |= CollisionCategories.Body;
433 m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
434 }
406 435
407 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); 436 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
408 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); 437 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
@@ -774,8 +803,16 @@ namespace OpenSim.Region.Physics.OdePlugin
774 m_collisionCategories &= ~CollisionCategories.Body; 803 m_collisionCategories &= ~CollisionCategories.Body;
775 m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land); 804 m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);
776 805
777 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); 806 if (m_assetFailed)
778 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); 807 {
808 d.GeomSetCategoryBits(prim_geom, 0);
809 d.GeomSetCollideBits(prim_geom, 0);
810 }
811 else
812 {
813 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
814 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
815 }
779 816
780 d.BodyDestroy(Body); 817 d.BodyDestroy(Body);
781 lock (childrenPrim) 818 lock (childrenPrim)
@@ -799,8 +836,17 @@ namespace OpenSim.Region.Physics.OdePlugin
799 m_collisionCategories &= ~CollisionCategories.Body; 836 m_collisionCategories &= ~CollisionCategories.Body;
800 m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land); 837 m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);
801 838
802 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); 839 if (m_assetFailed)
803 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); 840 {
841 d.GeomSetCategoryBits(prim_geom, 0);
842 d.GeomSetCollideBits(prim_geom, 0);
843 }
844 else
845 {
846
847 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
848 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
849 }
804 850
805 Body = IntPtr.Zero; 851 Body = IntPtr.Zero;
806 } 852 }
@@ -1090,8 +1136,16 @@ Console.WriteLine("ZProcessTaints for " + Name);
1090 prm.m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); 1136 prm.m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
1091 1137
1092//Console.WriteLine(" GeomSetCategoryBits 1: " + prm.prim_geom + " - " + (int)prm.m_collisionCategories + " for " + Name); 1138//Console.WriteLine(" GeomSetCategoryBits 1: " + prm.prim_geom + " - " + (int)prm.m_collisionCategories + " for " + Name);
1093 d.GeomSetCategoryBits(prm.prim_geom, (int)prm.m_collisionCategories); 1139 if (prm.m_assetFailed)
1094 d.GeomSetCollideBits(prm.prim_geom, (int)prm.m_collisionFlags); 1140 {
1141 d.GeomSetCategoryBits(prm.prim_geom, 0);
1142 d.GeomSetCollideBits(prm.prim_geom, prm.BadMeshAssetCollideBits);
1143 }
1144 else
1145 {
1146 d.GeomSetCategoryBits(prm.prim_geom, (int)prm.m_collisionCategories);
1147 d.GeomSetCollideBits(prm.prim_geom, (int)prm.m_collisionFlags);
1148 }
1095 1149
1096 d.Quaternion quat = new d.Quaternion(); 1150 d.Quaternion quat = new d.Quaternion();
1097 quat.W = prm._orientation.W; 1151 quat.W = prm._orientation.W;
@@ -1136,10 +1190,18 @@ Console.WriteLine("ZProcessTaints for " + Name);
1136 m_collisionCategories |= CollisionCategories.Body; 1190 m_collisionCategories |= CollisionCategories.Body;
1137 m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); 1191 m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
1138 1192
1139//Console.WriteLine("GeomSetCategoryBits 2: " + prim_geom + " - " + (int)m_collisionCategories + " for " + Name); 1193 if (m_assetFailed)
1140 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); 1194 {
1141//Console.WriteLine(" Post GeomSetCategoryBits 2"); 1195 d.GeomSetCategoryBits(prim_geom, 0);
1142 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); 1196 d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
1197 }
1198 else
1199 {
1200 //Console.WriteLine("GeomSetCategoryBits 2: " + prim_geom + " - " + (int)m_collisionCategories + " for " + Name);
1201 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1202 //Console.WriteLine(" Post GeomSetCategoryBits 2");
1203 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1204 }
1143 1205
1144 d.Quaternion quat2 = new d.Quaternion(); 1206 d.Quaternion quat2 = new d.Quaternion();
1145 quat2.W = _orientation.W; 1207 quat2.W = _orientation.W;
@@ -1300,8 +1362,16 @@ Console.WriteLine("ZProcessTaints for " + Name);
1300 disableBodySoft(); 1362 disableBodySoft();
1301 } 1363 }
1302 1364
1303 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); 1365 if (m_assetFailed)
1304 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); 1366 {
1367 d.GeomSetCategoryBits(prim_geom, 0);
1368 d.GeomSetCollideBits(prim_geom, 0);
1369 }
1370 else
1371 {
1372 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1373 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1374 }
1305 1375
1306 if (IsPhysical) 1376 if (IsPhysical)
1307 { 1377 {
@@ -1322,8 +1392,16 @@ Console.WriteLine("ZProcessTaints for " + Name);
1322 if (m_collidesWater) 1392 if (m_collidesWater)
1323 m_collisionFlags |= CollisionCategories.Water; 1393 m_collisionFlags |= CollisionCategories.Water;
1324 1394
1325 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); 1395 if (m_assetFailed)
1326 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); 1396 {
1397 d.GeomSetCategoryBits(prim_geom, 0);
1398 d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
1399 }
1400 else
1401 {
1402 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1403 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1404 }
1327 1405
1328 if (IsPhysical) 1406 if (IsPhysical)
1329 { 1407 {
@@ -1498,6 +1576,10 @@ Console.WriteLine("CreateGeom:");
1498 mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, _parent_scene.meshSculptLOD, IsPhysical); 1576 mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, _parent_scene.meshSculptLOD, IsPhysical);
1499 // createmesh returns null when it's a shape that isn't a cube. 1577 // createmesh returns null when it's a shape that isn't a cube.
1500 // m_log.Debug(m_localID); 1578 // m_log.Debug(m_localID);
1579 if (mesh == null)
1580 CheckMeshAsset();
1581 else
1582 m_assetFailed = false;
1501 } 1583 }
1502 1584
1503#if SPAM 1585#if SPAM
@@ -1997,7 +2079,14 @@ Console.WriteLine(" JointCreateFixed");
1997 // Don't need to re-enable body.. it's done in SetMesh 2079 // Don't need to re-enable body.. it's done in SetMesh
1998 2080
1999 if (_parent_scene.needsMeshing(_pbs)) 2081 if (_parent_scene.needsMeshing(_pbs))
2082 {
2000 mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical); 2083 mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical);
2084 if (mesh == null)
2085 CheckMeshAsset();
2086 else
2087 m_assetFailed = false;
2088 }
2089
2001 } 2090 }
2002 2091
2003 CreateGeom(m_targetSpace, mesh); 2092 CreateGeom(m_targetSpace, mesh);
@@ -2049,14 +2138,19 @@ Console.WriteLine(" JointCreateFixed");
2049 m_collisionFlags &= ~CollisionCategories.Water; 2138 m_collisionFlags &= ~CollisionCategories.Water;
2050 } 2139 }
2051 2140
2052 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); 2141 if (m_assetFailed)
2053 } 2142 d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
2143 else
2054 2144
2145 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
2146 }
2055 /// <summary> 2147 /// <summary>
2056 /// Change prim in response to a shape taint. 2148 /// Change prim in response to a shape taint.
2057 /// </summary> 2149 /// </summary>
2058 private void changeshape() 2150 private void changeshape()
2059 { 2151 {
2152 m_taintshape = false;
2153
2060 // Cleanup of old prim geometry and Bodies 2154 // Cleanup of old prim geometry and Bodies
2061 if (IsPhysical && Body != IntPtr.Zero) 2155 if (IsPhysical && Body != IntPtr.Zero)
2062 { 2156 {
@@ -2084,6 +2178,7 @@ Console.WriteLine(" JointCreateFixed");
2084 2178
2085 IMesh mesh = null; 2179 IMesh mesh = null;
2086 2180
2181
2087 if (_parent_scene.needsMeshing(_pbs)) 2182 if (_parent_scene.needsMeshing(_pbs))
2088 { 2183 {
2089 // Don't need to re-enable body.. it's done in CreateMesh 2184 // Don't need to re-enable body.. it's done in CreateMesh
@@ -2094,6 +2189,10 @@ Console.WriteLine(" JointCreateFixed");
2094 2189
2095 // createmesh returns null when it doesn't mesh. 2190 // createmesh returns null when it doesn't mesh.
2096 mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical); 2191 mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical);
2192 if (mesh == null)
2193 CheckMeshAsset();
2194 else
2195 m_assetFailed = false;
2097 } 2196 }
2098 2197
2099 CreateGeom(m_targetSpace, mesh); 2198 CreateGeom(m_targetSpace, mesh);
@@ -2130,7 +2229,7 @@ Console.WriteLine(" JointCreateFixed");
2130 } 2229 }
2131 2230
2132 resetCollisionAccounting(); 2231 resetCollisionAccounting();
2133 m_taintshape = false; 2232// m_taintshape = false;
2134 } 2233 }
2135 2234
2136 /// <summary> 2235 /// <summary>
@@ -2396,6 +2495,7 @@ Console.WriteLine(" JointCreateFixed");
2396 set 2495 set
2397 { 2496 {
2398 _pbs = value; 2497 _pbs = value;
2498 m_assetFailed = false;
2399 m_taintshape = true; 2499 m_taintshape = true;
2400 } 2500 }
2401 } 2501 }
@@ -3234,5 +3334,37 @@ Console.WriteLine(" JointCreateFixed");
3234 { 3334 {
3235 m_material = pMaterial; 3335 m_material = pMaterial;
3236 } 3336 }
3337
3338
3339 private void CheckMeshAsset()
3340 {
3341 if (_pbs.SculptEntry && !m_assetFailed && _pbs.SculptTexture != UUID.Zero)
3342 {
3343 m_assetFailed = true;
3344 Util.FireAndForget(delegate
3345 {
3346 RequestAssetDelegate assetProvider = _parent_scene.RequestAssetMethod;
3347 if (assetProvider != null)
3348 assetProvider(_pbs.SculptTexture, MeshAssetReveived);
3349 });
3350 }
3351 }
3352
3353 void MeshAssetReveived(AssetBase asset)
3354 {
3355 if (asset.Data != null && asset.Data.Length > 0)
3356 {
3357 if (!_pbs.SculptEntry)
3358 return;
3359 if (_pbs.SculptTexture.ToString() != asset.ID)
3360 return;
3361
3362 _pbs.SculptData = new byte[asset.Data.Length];
3363 asset.Data.CopyTo(_pbs.SculptData, 0);
3364// m_assetFailed = false;
3365 m_taintshape = true;
3366 _parent_scene.AddPhysicsActorTaint(this);
3367 }
3368 }
3237 } 3369 }
3238} \ No newline at end of file 3370} \ No newline at end of file
diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
index cbe21e2..d53bd90 100644
--- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
+++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
@@ -4320,4 +4320,4 @@ namespace OpenSim.Region.Physics.OdePlugin
4320 m_stats[ODEPrimUpdateFrameMsStatName] = 0; 4320 m_stats[ODEPrimUpdateFrameMsStatName] = 0;
4321 } 4321 }
4322 } 4322 }
4323} \ No newline at end of file 4323}
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
index 7620df3..cf801ba 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
@@ -56,6 +56,7 @@ using GridRegion = OpenSim.Services.Interfaces.GridRegion;
56using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; 56using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
57using PrimType = OpenSim.Region.Framework.Scenes.PrimType; 57using PrimType = OpenSim.Region.Framework.Scenes.PrimType;
58using AssetLandmark = OpenSim.Framework.AssetLandmark; 58using AssetLandmark = OpenSim.Framework.AssetLandmark;
59using RegionFlags = OpenSim.Framework.RegionFlags;
59 60
60using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; 61using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
61using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; 62using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
@@ -3772,6 +3773,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
3772 } 3773 }
3773 3774
3774 /// <summary> 3775 /// <summary>
3776 /// Returns the name of the child prim or seated avatar matching the
3777 /// specified link number.
3778 /// </summary>
3779 /// <param name="linknum">
3780 /// The number of a link in the linkset or a link-related constant.
3781 /// </param>
3782 /// <returns>
3783 /// The name determined to match the specified link number.
3784 /// </returns>
3785 /// <remarks>
3775 /// The rules governing the returned name are not simple. The only 3786 /// The rules governing the returned name are not simple. The only
3776 /// time a blank name is returned is if the target prim has a blank 3787 /// time a blank name is returned is if the target prim has a blank
3777 /// name. If no prim with the given link number can be found then 3788 /// name. If no prim with the given link number can be found then
@@ -3799,10 +3810,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
3799 /// Mentions NULL_KEY being returned 3810 /// Mentions NULL_KEY being returned
3800 /// http://wiki.secondlife.com/wiki/LlGetLinkName 3811 /// http://wiki.secondlife.com/wiki/LlGetLinkName
3801 /// Mentions using the LINK_* constants, some of which are negative 3812 /// Mentions using the LINK_* constants, some of which are negative
3802 /// </summary> 3813 /// </remarks>
3803 public LSL_String llGetLinkName(int linknum) 3814 public LSL_String llGetLinkName(int linknum)
3804 { 3815 {
3805 m_host.AddScriptLPS(1); 3816 m_host.AddScriptLPS(1);
3817 // simplest case, this prims link number
3818 if (linknum == m_host.LinkNum || linknum == ScriptBaseClass.LINK_THIS)
3819 return m_host.Name;
3820
3806 // parse for sitting avatare-names 3821 // parse for sitting avatare-names
3807 List<String> nametable = new List<String>(); 3822 List<String> nametable = new List<String>();
3808 World.ForEachRootScenePresence(delegate(ScenePresence presence) 3823 World.ForEachRootScenePresence(delegate(ScenePresence presence)
@@ -3826,10 +3841,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
3826 return nametable[totalprims - linknum]; 3841 return nametable[totalprims - linknum];
3827 } 3842 }
3828 3843
3829 // simplest case, this prims link number
3830 if (m_host.LinkNum == linknum)
3831 return m_host.Name;
3832
3833 // Single prim 3844 // Single prim
3834 if (m_host.LinkNum == 0) 3845 if (m_host.LinkNum == 0)
3835 { 3846 {
@@ -6458,7 +6469,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
6458 GridInstantMessage msg = new GridInstantMessage(World, 6469 GridInstantMessage msg = new GridInstantMessage(World,
6459 m_host.OwnerID, m_host.Name, destID, 6470 m_host.OwnerID, m_host.Name, destID,
6460 (byte)InstantMessageDialog.TaskInventoryOffered, 6471 (byte)InstantMessageDialog.TaskInventoryOffered,
6461 false, string.Format("'{0}'"), 6472 false, string.Format("'{0}'", category),
6462// We won't go so far as to add a SLURL, but this is the format used by LL as of 2012-10-06 6473// We won't go so far as to add a SLURL, but this is the format used by LL as of 2012-10-06
6463// false, string.Format("'{0}' ( http://slurl.com/secondlife/{1}/{2}/{3}/{4} )", category, World.Name, (int)pos.X, (int)pos.Y, (int)pos.Z), 6474// false, string.Format("'{0}' ( http://slurl.com/secondlife/{1}/{2}/{3}/{4} )", category, World.Name, (int)pos.X, (int)pos.Y, (int)pos.Z),
6464 folderID, false, pos, 6475 folderID, false, pos,
@@ -9313,11 +9324,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
9313 9324
9314 GridRegion info; 9325 GridRegion info;
9315 9326
9316 if (m_ScriptEngine.World.RegionInfo.RegionName == simulator) //Det data for this simulator? 9327 if (World.RegionInfo.RegionName == simulator)
9317 9328 info = new GridRegion(World.RegionInfo);
9318 info = new GridRegion(m_ScriptEngine.World.RegionInfo);
9319 else 9329 else
9320 info = m_ScriptEngine.World.GridService.GetRegionByName(m_ScriptEngine.World.RegionInfo.ScopeID, simulator); 9330 info = World.GridService.GetRegionByName(m_ScriptEngine.World.RegionInfo.ScopeID, simulator);
9321 9331
9322 switch (data) 9332 switch (data)
9323 { 9333 {
@@ -9327,9 +9337,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
9327 ScriptSleep(1000); 9337 ScriptSleep(1000);
9328 return UUID.Zero.ToString(); 9338 return UUID.Zero.ToString();
9329 } 9339 }
9330 if (m_ScriptEngine.World.RegionInfo.RegionName != simulator) 9340
9341 bool isHypergridRegion = false;
9342
9343 if (World.RegionInfo.RegionName != simulator && info.RegionSecret != "")
9344 {
9345 // Hypergrid is currently placing real destination region co-ords into RegionSecret.
9346 // But other code can also use this field for a genuine RegionSecret! Therefore, if
9347 // anything is present we need to disambiguate.
9348 //
9349 // FIXME: Hypergrid should be storing this data in a different field.
9350 RegionFlags regionFlags
9351 = (RegionFlags)m_ScriptEngine.World.GridService.GetRegionFlags(
9352 info.ScopeID, info.RegionID);
9353 isHypergridRegion = (regionFlags & RegionFlags.Hyperlink) != 0;
9354 }
9355
9356 if (isHypergridRegion)
9331 { 9357 {
9332 //Hypergrid Region co-ordinates
9333 uint rx = 0, ry = 0; 9358 uint rx = 0, ry = 0;
9334 Utils.LongToUInts(Convert.ToUInt64(info.RegionSecret), out rx, out ry); 9359 Utils.LongToUInts(Convert.ToUInt64(info.RegionSecret), out rx, out ry);
9335 9360
@@ -9340,7 +9365,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
9340 } 9365 }
9341 else 9366 else
9342 { 9367 {
9343 //Local-cooridnates 9368 // Local grid co-oridnates
9344 reply = new LSL_Vector( 9369 reply = new LSL_Vector(
9345 info.RegionLocX, 9370 info.RegionLocX,
9346 info.RegionLocY, 9371 info.RegionLocY,
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs
index 31be450..29bc163 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs
@@ -3545,17 +3545,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
3545 /// <returns></returns> 3545 /// <returns></returns>
3546 public void osSetContentType(LSL_Key id, string type) 3546 public void osSetContentType(LSL_Key id, string type)
3547 { 3547 {
3548 CheckThreatLevel(ThreatLevel.High,"osSetResponseType"); 3548 CheckThreatLevel(ThreatLevel.High, "osSetContentType");
3549
3549 if (m_UrlModule != null) 3550 if (m_UrlModule != null)
3550 m_UrlModule.HttpContentType(new UUID(id),type); 3551 m_UrlModule.HttpContentType(new UUID(id),type);
3551 } 3552 }
3553
3552 /// Shout an error if the object owner did not grant the script the specified permissions. 3554 /// Shout an error if the object owner did not grant the script the specified permissions.
3553 /// </summary> 3555 /// </summary>
3554 /// <param name="perms"></param> 3556 /// <param name="perms"></param>
3555 /// <returns>boolean indicating whether an error was shouted.</returns> 3557 /// <returns>boolean indicating whether an error was shouted.</returns>
3556 protected bool ShoutErrorOnLackingOwnerPerms(int perms, string errorPrefix) 3558 protected bool ShoutErrorOnLackingOwnerPerms(int perms, string errorPrefix)
3557 { 3559 {
3558 CheckThreatLevel(ThreatLevel.Moderate, "osDropAttachment");
3559 m_host.AddScriptLPS(1); 3560 m_host.AddScriptLPS(1);
3560 bool fail = false; 3561 bool fail = false;
3561 if (m_item.PermsGranter != m_host.OwnerID) 3562 if (m_item.PermsGranter != m_host.OwnerID)
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs
index 60a7e14..62bd6b8 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs
@@ -612,6 +612,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
612 public const int CLICK_ACTION_OPEN = 4; 612 public const int CLICK_ACTION_OPEN = 4;
613 public const int CLICK_ACTION_PLAY = 5; 613 public const int CLICK_ACTION_PLAY = 5;
614 public const int CLICK_ACTION_OPEN_MEDIA = 6; 614 public const int CLICK_ACTION_OPEN_MEDIA = 6;
615 public const int CLICK_ACTION_ZOOM = 7;
615 616
616 // constants for the llDetectedTouch* functions 617 // constants for the llDetectedTouch* functions
617 public const int TOUCH_INVALID_FACE = -1; 618 public const int TOUCH_INVALID_FACE = -1;
diff --git a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs
index 3fd0c53..6d5ce4b 100644
--- a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs
+++ b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs
@@ -128,7 +128,7 @@ namespace OpenSim.Services.Connectors.Friends
128 return Call(region, sendData); 128 return Call(region, sendData);
129 } 129 }
130 130
131 public bool StatusNotify(GridRegion region, UUID userID, UUID friendID, bool online) 131 public bool StatusNotify(GridRegion region, UUID userID, string friendID, bool online)
132 { 132 {
133 Dictionary<string, object> sendData = new Dictionary<string, object>(); 133 Dictionary<string, object> sendData = new Dictionary<string, object>();
134 //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); 134 //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString();
@@ -136,7 +136,7 @@ namespace OpenSim.Services.Connectors.Friends
136 sendData["METHOD"] = "status"; 136 sendData["METHOD"] = "status";
137 137
138 sendData["FromID"] = userID.ToString(); 138 sendData["FromID"] = userID.ToString();
139 sendData["ToID"] = friendID.ToString(); 139 sendData["ToID"] = friendID;
140 sendData["Online"] = online.ToString(); 140 sendData["Online"] = online.ToString();
141 141
142 return Call(region, sendData); 142 return Call(region, sendData);
@@ -153,7 +153,7 @@ namespace OpenSim.Services.Connectors.Friends
153 if (!region.ServerURI.EndsWith("/")) 153 if (!region.ServerURI.EndsWith("/"))
154 path = "/" + path; 154 path = "/" + path;
155 string uri = region.ServerURI + path; 155 string uri = region.ServerURI + path;
156 m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: calling {0}", uri); 156// m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: calling {0}", uri);
157 157
158 try 158 try
159 { 159 {
diff --git a/OpenSim/Services/HypergridService/HGFriendsService.cs b/OpenSim/Services/HypergridService/HGFriendsService.cs
index 98423d7..a8bcfb2 100644
--- a/OpenSim/Services/HypergridService/HGFriendsService.cs
+++ b/OpenSim/Services/HypergridService/HGFriendsService.cs
@@ -397,7 +397,7 @@ namespace OpenSim.Services.HypergridService
397 if (region != null) 397 if (region != null)
398 { 398 {
399 m_log.DebugFormat("[HGFRIENDS SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline")); 399 m_log.DebugFormat("[HGFRIENDS SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline"));
400 m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID, online); 400 m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online);
401 } 401 }
402 } 402 }
403 } 403 }
diff --git a/OpenSim/Services/HypergridService/UserAgentService.cs b/OpenSim/Services/HypergridService/UserAgentService.cs
index a6fc731..a26a922 100644
--- a/OpenSim/Services/HypergridService/UserAgentService.cs
+++ b/OpenSim/Services/HypergridService/UserAgentService.cs
@@ -504,7 +504,7 @@ namespace OpenSim.Services.HypergridService
504 if (region != null) 504 if (region != null)
505 { 505 {
506 m_log.DebugFormat("[USER AGENT SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline")); 506 m_log.DebugFormat("[USER AGENT SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline"));
507 m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID, online); 507 m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online);
508 } 508 }
509 } 509 }
510 } 510 }
diff --git a/OpenSim/Services/Interfaces/IPresenceService.cs b/OpenSim/Services/Interfaces/IPresenceService.cs
index 8d583ff..90f9842 100644
--- a/OpenSim/Services/Interfaces/IPresenceService.cs
+++ b/OpenSim/Services/Interfaces/IPresenceService.cs
@@ -61,13 +61,49 @@ namespace OpenSim.Services.Interfaces
61 61
62 public interface IPresenceService 62 public interface IPresenceService
63 { 63 {
64 /// <summary>
65 /// Store session information.
66 /// </summary>
67 /// <returns>/returns>
68 /// <param name='userID'></param>
69 /// <param name='sessionID'></param>
70 /// <param name='secureSessionID'></param>
64 bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID); 71 bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID);
72
73 /// <summary>
74 /// Remove session information.
75 /// </summary>
76 /// <returns></returns>
77 /// <param name='sessionID'></param>
65 bool LogoutAgent(UUID sessionID); 78 bool LogoutAgent(UUID sessionID);
79
80 /// <summary>
81 /// Remove session information for all agents in the given region.
82 /// </summary>
83 /// <returns></returns>
84 /// <param name='regionID'></param>
66 bool LogoutRegionAgents(UUID regionID); 85 bool LogoutRegionAgents(UUID regionID);
67 86
87 /// <summary>
88 /// Update data for an existing session.
89 /// </summary>
90 /// <returns></returns>
91 /// <param name='sessionID'></param>
92 /// <param name='regionID'></param>
68 bool ReportAgent(UUID sessionID, UUID regionID); 93 bool ReportAgent(UUID sessionID, UUID regionID);
69 94
95 /// <summary>
96 /// Get session information for a given session ID.
97 /// </summary>
98 /// <returns></returns>
99 /// <param name='sessionID'></param>
70 PresenceInfo GetAgent(UUID sessionID); 100 PresenceInfo GetAgent(UUID sessionID);
101
102 /// <summary>
103 /// Get session information for a collection of users.
104 /// </summary>
105 /// <returns>Session information for the users.</returns>
106 /// <param name='userIDs'></param>
71 PresenceInfo[] GetAgents(string[] userIDs); 107 PresenceInfo[] GetAgents(string[] userIDs);
72 } 108 }
73} 109} \ No newline at end of file