diff options
author | BlueWall | 2012-10-20 05:25:58 -0400 |
---|---|---|
committer | BlueWall | 2012-10-20 05:25:58 -0400 |
commit | 07f1d44174f5e2b4e3d5ffdec3619bb84f357af2 (patch) | |
tree | 83cc0442b2c1b89a5b3dd94cff23d5098415777e /OpenSim/Region | |
parent | Merge branch 'master' into connector_plugin (diff) | |
parent | Fix: invinite loading for Viewer3 : parcelinfo request of traffic-value (impl... (diff) | |
download | opensim-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/Region')
43 files changed, 2326 insertions, 1172 deletions
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; | |||
37 | using Nini.Config; | 37 | using Nini.Config; |
38 | using OpenMetaverse.Packets; | 38 | using OpenMetaverse.Packets; |
39 | using OpenSim.Framework; | 39 | using OpenSim.Framework; |
40 | using OpenSim.Framework.Console; | ||
40 | using OpenSim.Framework.Monitoring; | 41 | using OpenSim.Framework.Monitoring; |
41 | using OpenSim.Region.Framework.Scenes; | 42 | using OpenSim.Region.Framework.Scenes; |
42 | using OpenMetaverse; | 43 | using 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; | |||
30 | using System.Net.Sockets; | 30 | using System.Net.Sockets; |
31 | using System.Threading; | 31 | using System.Threading; |
32 | using log4net; | 32 | using log4net; |
33 | using OpenSim.Framework; | ||
33 | 34 | ||
34 | namespace OpenMetaverse | 35 | namespace 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; | |||
31 | using OpenMetaverse; | 31 | using OpenMetaverse; |
32 | using OpenMetaverse.Packets; | 32 | using OpenMetaverse.Packets; |
33 | using log4net; | 33 | using log4net; |
34 | using OpenSim.Framework.Monitoring; | ||
34 | 35 | ||
35 | namespace OpenSim.Region.ClientStack.LindenUDP | 36 | namespace 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 @@ | |||
28 | using System; | 28 | using System; |
29 | using System.Collections; | 29 | using System.Collections; |
30 | using System.Collections.Generic; | 30 | using System.Collections.Generic; |
31 | using System.Linq; | ||
31 | using System.Reflection; | 32 | using System.Reflection; |
32 | using System.Threading; | 33 | using System.Threading; |
33 | using log4net; | 34 | using 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 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Linq; | ||
30 | using System.Reflection; | 31 | using System.Reflection; |
31 | using System.Text; | 32 | using System.Text; |
32 | using System.Text.RegularExpressions; | 33 | using 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 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Linq; | ||
30 | using System.Reflection; | 31 | using System.Reflection; |
31 | using log4net; | 32 | using log4net; |
32 | using Mono.Addins; | 33 | using Mono.Addins; |
@@ -36,6 +37,8 @@ using OpenMetaverse.StructuredData; | |||
36 | using OpenSim.Framework; | 37 | using OpenSim.Framework; |
37 | using OpenSim.Region.Framework.Interfaces; | 38 | using OpenSim.Region.Framework.Interfaces; |
38 | using OpenSim.Region.Framework.Scenes; | 39 | using OpenSim.Region.Framework.Scenes; |
40 | using OpenSim.Services.Interfaces; | ||
41 | using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; | ||
39 | 42 | ||
40 | namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | 43 | namespace 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; | |||
39 | using OpenMetaverse; | 39 | using 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)] |
427 | public static extern string GetVersion(); | 431 | public 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] |
430 | public static extern uint Initialize(Vector3 maxPosition, IntPtr parms, | 435 | public 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] |
531 | public static extern void DumpBulletStatistics(); | 536 | public 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] |
535 | public static extern void SetDebugLogCallback(DebugLogCallback callback); | 540 | public 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] |
561 | public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms, | 566 | public 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] |
566 | public static extern bool UpdateParameter2(IntPtr world, uint localID, String parm, float value); | 572 | public 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) | |||
602 | public static extern bool IsNativeShape2(IntPtr shape); | 608 | public static extern bool IsNativeShape2(IntPtr shape); |
603 | 609 | ||
604 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | 610 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] |
611 | public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale); | ||
612 | |||
613 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
605 | public static extern IntPtr CreateCompoundShape2(IntPtr sim); | 614 | public 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); | |||
1037 | public static extern int GetNumConstraintRefs2(IntPtr obj); | 1046 | public static extern int GetNumConstraintRefs2(IntPtr obj); |
1038 | 1047 | ||
1039 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | 1048 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] |
1040 | public static extern Vector3 GetDeltaLinearVelocity2(IntPtr obj); | ||
1041 | |||
1042 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
1043 | public static extern Vector3 GetDeltaAngularVelocity2(IntPtr obj); | ||
1044 | |||
1045 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
1046 | public static extern Vector3 GetPushVelocity2(IntPtr obj); | ||
1047 | |||
1048 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
1049 | public static extern Vector3 GetTurnVelocity2(IntPtr obj); | ||
1050 | |||
1051 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
1052 | public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask); | 1049 | public 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); | |||
1108 | public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject); | 1105 | public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject); |
1109 | 1106 | ||
1110 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | 1107 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] |
1108 | public static extern void DumpCollisionShape2(IntPtr sim, IntPtr collisionShape); | ||
1109 | |||
1110 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
1111 | public static extern void DumpConstraint2(IntPtr sim, IntPtr constrain); | ||
1112 | |||
1113 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
1114 | public static extern void DumpAllInfo2(IntPtr sim); | ||
1115 | |||
1116 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
1111 | public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo); | 1117 | public 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; | |||
56 | using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; | 56 | using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; |
57 | using PrimType = OpenSim.Region.Framework.Scenes.PrimType; | 57 | using PrimType = OpenSim.Region.Framework.Scenes.PrimType; |
58 | using AssetLandmark = OpenSim.Framework.AssetLandmark; | 58 | using AssetLandmark = OpenSim.Framework.AssetLandmark; |
59 | using RegionFlags = OpenSim.Framework.RegionFlags; | ||
59 | 60 | ||
60 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | 61 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; |
61 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | 62 | using 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; |