diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index 0da7743274..f4b9da8bca 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -95,7 +95,6 @@ namespace OpenSim.Framework tryGetRet = m_clients.TryGetValue(circuitCode, out client); if (tryGetRet) { - //m_log.Debug("[ClientManager]: Processing IN packet " + packet.Type.ToString()); client.InPacket(packet); } else diff --git a/OpenSim/Framework/Communications/Cache/AssetCache.cs b/OpenSim/Framework/Communications/Cache/AssetCache.cs index 15b52b047a..e3a4eb8193 100644 --- a/OpenSim/Framework/Communications/Cache/AssetCache.cs +++ b/OpenSim/Framework/Communications/Cache/AssetCache.cs @@ -250,7 +250,9 @@ namespace OpenSim.Framework.Communications.Cache } else { - m_log.DebugFormat("[ASSET CACHE]: Adding request for {0} {1}", isTexture ? "texture" : "asset", assetId); + #if DEBUG + //m_log.DebugFormat("[ASSET CACHE]: Adding request for {0} {1}", isTexture ? "texture" : "asset", assetId); + #endif NewAssetRequest req = new NewAssetRequest(assetId, callback); @@ -384,14 +386,17 @@ namespace OpenSim.Framework.Communications.Cache } } } - - m_log.DebugFormat("[ASSET CACHE]: Adding {0} {1} [{2}]: {3}.", temporary, type, asset.FullID, result); + #if DEBUG + //m_log.DebugFormat("[ASSET CACHE]: Adding {0} {1} [{2}]: {3}.", temporary, type, asset.FullID, result); + #endif } // See IAssetReceiver public void AssetReceived(AssetBase asset, bool IsTexture) { - m_log.DebugFormat("[ASSET CACHE]: Recieved {0} [{1}]", IsTexture ? "texture" : "asset", asset.FullID); + #if DEBUG + m_log.DebugFormat("[ASSET CACHE]: Received {0} [{1}]", IsTexture ? "texture" : "asset", asset.FullID); + #endif if (asset.FullID != LLUUID.Zero) // if it is set to zero then the asset wasn't found by the server { @@ -405,7 +410,9 @@ namespace OpenSim.Framework.Communications.Cache TextureImage image = new TextureImage(asset); if (Textures.ContainsKey(image.FullID)) { - m_log.DebugFormat("[ASSET CACHE]: There's already an texture {0} in memory. Skipping.", asset.FullID); + #if DEBUG + //m_log.DebugFormat("[ASSET CACHE]: There's already an texture {0} in memory. Skipping.", asset.FullID); + #endif } else { @@ -422,7 +429,9 @@ namespace OpenSim.Framework.Communications.Cache AssetInfo assetInf = new AssetInfo(asset); if (Assets.ContainsKey(assetInf.FullID)) { - m_log.DebugFormat("[ASSET CACHE]: There's already an asset {0} in memory. Skipping.", asset.FullID); + #if DEBUG + //m_log.DebugFormat("[ASSET CACHE]: There's already an asset {0} in memory. Skipping.", asset.FullID); + #endif } else { @@ -435,7 +444,9 @@ namespace OpenSim.Framework.Communications.Cache if (RequestedAssets.ContainsKey(assetInf.FullID)) { - m_log.DebugFormat("[ASSET CACHE]: Moving {0} from RequestedAssets to AssetRequests", asset.FullID); + #if DEBUG + //m_log.DebugFormat("[ASSET CACHE]: Moving {0} from RequestedAssets to AssetRequests", asset.FullID); + #endif AssetRequest req = RequestedAssets[assetInf.FullID]; req.AssetInf = assetInf; diff --git a/OpenSim/Framework/Communications/Cache/AssetServerBase.cs b/OpenSim/Framework/Communications/Cache/AssetServerBase.cs index cae42319c8..4b5df5c066 100644 --- a/OpenSim/Framework/Communications/Cache/AssetServerBase.cs +++ b/OpenSim/Framework/Communications/Cache/AssetServerBase.cs @@ -137,7 +137,9 @@ namespace OpenSim.Framework.Communications.Cache req.IsTexture = isTexture; m_assetRequests.Enqueue(req); + #if DEBUG m_log.InfoFormat("[ASSET SERVER]: Added {0} to request queue", assetID); + #endif } public virtual void UpdateAsset(AssetBase asset) diff --git a/OpenSim/Framework/Communications/Cache/GridAssetClient.cs b/OpenSim/Framework/Communications/Cache/GridAssetClient.cs index 8114eb041d..baa60587b1 100644 --- a/OpenSim/Framework/Communications/Cache/GridAssetClient.cs +++ b/OpenSim/Framework/Communications/Cache/GridAssetClient.cs @@ -52,7 +52,9 @@ namespace OpenSim.Framework.Communications.Cache Stream s = null; try { - m_log.DebugFormat("[ASSETCACHE]: Querying for {0}", req.AssetID.ToString()); + #if DEBUG + //m_log.DebugFormat("[GRID ASSET CLIENT]: Querying for {0}", req.AssetID.ToString()); + #endif RestClient rc = new RestClient(_assetServerUrl); rc.AddResourcePath("assets"); @@ -72,9 +74,9 @@ namespace OpenSim.Framework.Communications.Cache } catch (Exception e) { - m_log.Error("[ASSETCACHE]: " + e.Message); - m_log.DebugFormat("[ASSETCACHE]: Getting asset {0}", req.AssetID.ToString()); - m_log.Error("[ASSETCACHE]: " + e.StackTrace); + m_log.Error("[GRID ASSET CLIENT]: " + e.Message); + m_log.DebugFormat("[GRID ASSET CLIENT]: Getting asset {0}", req.AssetID.ToString()); + m_log.Error("[GRID ASSET CLIENT]: " + e.StackTrace); } return null; @@ -95,19 +97,19 @@ namespace OpenSim.Framework.Communications.Cache // XmlSerializer xs = new XmlSerializer(typeof(AssetBase)); // xs.Serialize(s, asset); // RestClient rc = new RestClient(_assetServerUrl); - m_log.Info("[ASSET]: Storing asset"); + m_log.Info("[GRID ASSET CLIENT]: Storing asset"); //rc.AddResourcePath("assets"); // rc.RequestMethod = "POST"; // rc.Request(s); //m_log.InfoFormat("[ASSET]: Stored {0}", rc); - m_log.Info("[ASSET]: Sending to " + _assetServerUrl + "/assets/"); + m_log.Info("[GRID ASSET CLIENT]: Sending to " + _assetServerUrl + "/assets/"); RestObjectPoster.BeginPostObject(_assetServerUrl + "/assets/", asset); } catch (Exception e) { - m_log.Error("[ASSETS]: " + e.Message); + m_log.Error("[GRID ASSET CLIENT]: " + e.Message); } } @@ -122,4 +124,4 @@ namespace OpenSim.Framework.Communications.Cache #endregion } -} \ No newline at end of file +} diff --git a/OpenSim/Framework/Statistics/SimExtraStatsReporter.cs b/OpenSim/Framework/Statistics/SimExtraStatsReporter.cs index c8b8223ac5..a9a4b4e453 100644 --- a/OpenSim/Framework/Statistics/SimExtraStatsReporter.cs +++ b/OpenSim/Framework/Statistics/SimExtraStatsReporter.cs @@ -26,12 +26,19 @@ * */ +using System; +using System.Collections.Generic; +using System.Text; + using OpenSim.Framework; +using OpenSim.Framework.Statistics.Interfaces; + +using libsecondlife; namespace OpenSim.Framework.Statistics { public class SimExtraStatsReporter - { + { private long assetsInCache; private long texturesInCache; private long assetCacheMemoryUsage; @@ -42,6 +49,12 @@ namespace OpenSim.Framework.Statistics public long AssetCacheMemoryUsage { get { return assetCacheMemoryUsage; } } public long TextureCacheMemoryUsage { get { return textureCacheMemoryUsage; } } + /// + /// Retain a dictionary of all packet queues stats reporters + /// + private IDictionary packetQueueStatsReporters + = new Dictionary(); + public void AddAsset(AssetBase asset) { assetsInCache++; @@ -56,19 +69,89 @@ namespace OpenSim.Framework.Statistics texturesInCache++; textureCacheMemoryUsage += image.Data.Length; } - } + } + + /// + /// Register as a packet queue stats provider + /// + /// An agent LLUUID + /// + public void RegisterPacketQueueStatsProvider(LLUUID uuid, IPullStatsProvider provider) + { + lock (packetQueueStatsReporters) + { + packetQueueStatsReporters[uuid] = new PacketQueueStatsReporter(provider); + } + } + + /// + /// Deregister a packet queue stats provider + /// + /// An agent LLUUID + public void DeregisterPacketQueueStatsProvider(LLUUID uuid) + { + lock (packetQueueStatsReporters) + { + packetQueueStatsReporters.Remove(uuid); + } + } /// /// Report back collected statistical information. /// /// public string Report() - { - return string.Format( + { + StringBuilder sb = new StringBuilder(Environment.NewLine); + sb.Append("ASSET CACHE STATISTICS"); + sb.Append(Environment.NewLine); + sb.Append( + string.Format( @"Asset cache contains {0,6} assets using {1,10:0.000}K -Texture cache contains {2,6} textures using {3,10:0.000}K", - AssetsInCache, AssetCacheMemoryUsage / 1024.0, - TexturesInCache, TextureCacheMemoryUsage / 1024.0); +Texture cache contains {2,6} textures using {3,10:0.000}K" + Environment.NewLine, + AssetsInCache, AssetCacheMemoryUsage / 1024.0, + TexturesInCache, TextureCacheMemoryUsage / 1024.0)); + + sb.Append(Environment.NewLine); + sb.Append("PACKET QUEUE STATISTICS"); + sb.Append(Environment.NewLine); + sb.Append("Agent UUID "); + sb.Append( + string.Format( + " {0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}", + "Send", "In", "Out", "Resend", "Land", "Wind", "Cloud", "Task", "Texture", "Asset")); + sb.Append(Environment.NewLine); + + foreach (LLUUID key in packetQueueStatsReporters.Keys) + { + sb.Append(string.Format("{0}: ", key)); + sb.Append(packetQueueStatsReporters[key].Report()); + sb.Append(Environment.NewLine); + } + + return sb.ToString(); } } + + /// + /// Pull packet queue stats from packet queues and report + /// + public class PacketQueueStatsReporter + { + private IPullStatsProvider m_statsProvider; + + public PacketQueueStatsReporter(IPullStatsProvider provider) + { + m_statsProvider = provider; + } + + /// + /// Report back collected statistical information. + /// + /// + public string Report() + { + return m_statsProvider.GetStats(); + } + } } diff --git a/OpenSim/Region/ClientStack/ClientView.cs b/OpenSim/Region/ClientStack/ClientView.cs index fffb667f56..eddce35c08 100644 --- a/OpenSim/Region/ClientStack/ClientView.cs +++ b/OpenSim/Region/ClientStack/ClientView.cs @@ -224,6 +224,7 @@ namespace OpenSim.Region.ClientStack private UpdateVector handler089 = null; //OnUpdatePrimGroupPosition; private UpdatePrimRotation handler090 = null; //OnUpdatePrimGroupRotation; private UpdatePrimGroupRotation handler091 = null; //OnUpdatePrimGroupMouseRotation; + private PacketStats handler093 = null; // OnPacketStats; /* Properties */ @@ -346,6 +347,7 @@ namespace OpenSim.Region.ClientStack ack_pack(usePacket); m_log.Debug("[ClientView]: Dealing with " + PausedPackets.Count.ToString() + " packets backlogged."); ReadyForPackets = true; + // stuff the paused packets BACK into the thread queue. :) while (PausedPackets.Count > 0) { @@ -2065,7 +2067,11 @@ namespace OpenSim.Region.ClientStack SetDefaultPrimPacketValues(objupdate); objupdate.UpdateFlags = flags; SetPrimPacketShapeData(objupdate, primShape); - + if ((primShape.PCode == 111) || (primShape.PCode == 255)) + { + objupdate.Data = new byte[1]; + objupdate.Data[0] = primShape.State; + } return objupdate; } @@ -2655,7 +2661,7 @@ namespace OpenSim.Region.ClientStack PausedPackets.Enqueue(NewPack); return; } - + m_packetsReceived++; // lock (m_packetQueue) { // Handle appended ACKs @@ -2833,9 +2839,10 @@ namespace OpenSim.Region.ClientStack protected void SendPacketStats() { - if (OnPacketStats != null) + handler093 = OnPacketStats; + if (handler093 != null) { - OnPacketStats(m_packetsReceived - m_lastPacketsReceivedSentToScene, m_packetsSent - m_lastPacketsSentSentToScene, m_unAckedBytes); + handler093(m_packetsReceived - m_lastPacketsReceivedSentToScene, m_packetsSent - m_lastPacketsSentSentToScene, m_unAckedBytes); m_lastPacketsReceivedSentToScene = m_packetsReceived; m_lastPacketsSentSentToScene = m_packetsSent; } diff --git a/OpenSim/Region/ClientStack/PacketQueue.cs b/OpenSim/Region/ClientStack/PacketQueue.cs index 2ee259bfc0..9592ae1668 100644 --- a/OpenSim/Region/ClientStack/PacketQueue.cs +++ b/OpenSim/Region/ClientStack/PacketQueue.cs @@ -29,13 +29,16 @@ using System; using System.Collections.Generic; using System.Threading; using System.Timers; +using libsecondlife; using libsecondlife.Packets; using OpenSim.Framework; +using OpenSim.Framework.Statistics; +using OpenSim.Framework.Statistics.Interfaces; using Timer=System.Timers.Timer; namespace OpenSim.Region.ClientStack { - public class PacketQueue + public class PacketQueue : IPullStatsProvider { private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); @@ -82,7 +85,7 @@ namespace OpenSim.Region.ClientStack // private long LastThrottle; // private long ThrottleInterval; private Timer throttleTimer; - + /// /// backreference so we can push packets out the client. May be temporary. /// @@ -129,6 +132,11 @@ namespace OpenSim.Region.ClientStack // TIMERS needed for this // LastThrottle = DateTime.Now.Ticks; // ThrottleInterval = (long)(throttletimems/throttleTimeDivisor); + + if (StatsManager.SimExtraStats != null) + { + StatsManager.SimExtraStats.RegisterPacketQueueStatsProvider(client.AgentId, this); + } } /* STANDARD QUEUE MANIPULATION INTERFACES */ @@ -136,49 +144,37 @@ namespace OpenSim.Region.ClientStack public void Enqueue(QueItem item) { - - // We could micro lock, but that will tend to actually - // probably be worse than just synchronizing on SendQueue - - // enqueue inbound right away. forget throttle checking inbound! - if (item.Incoming) + lock (this) { - SendQueue.Enqueue(item); - } - else - { - lock (this) + switch (item.throttleType) { - switch (item.throttleType) - { - case ThrottleOutPacketType.Resend: - ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item); - break; - case ThrottleOutPacketType.Texture: - ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item); - break; - case ThrottleOutPacketType.Task: - ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item); - break; - case ThrottleOutPacketType.Land: - ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item); - break; - case ThrottleOutPacketType.Asset: - ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item); - break; - case ThrottleOutPacketType.Cloud: - ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item); - break; - case ThrottleOutPacketType.Wind: - ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item); - break; + case ThrottleOutPacketType.Resend: + ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item); + break; + case ThrottleOutPacketType.Texture: + ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item); + break; + case ThrottleOutPacketType.Task: + ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item); + break; + case ThrottleOutPacketType.Land: + ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item); + break; + case ThrottleOutPacketType.Asset: + ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item); + break; + case ThrottleOutPacketType.Cloud: + ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item); + break; + case ThrottleOutPacketType.Wind: + ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item); + break; - default: - // Acknowledgements and other such stuff should go directly to the blocking Queue - // Throttling them may and likely 'will' be problematic - Client.ProcessOutPacket(item.Packet); - break; - } + default: + // Acknowledgements and other such stuff should go directly to the blocking Queue + // Throttling them may and likely 'will' be problematic + Client.ProcessOutPacket(item.Packet); + break; } } } @@ -227,6 +223,11 @@ namespace OpenSim.Region.ClientStack { m_enabled = false; throttleTimer.Stop(); + + if (StatsManager.SimExtraStats != null) + { + StatsManager.SimExtraStats.DeregisterPacketQueueStatsProvider(Client.AgentId); + } } private void ResetCounters() @@ -277,9 +278,6 @@ namespace OpenSim.Region.ClientStack if (ResendThrottle.UnderLimit() && ResendOutgoingPacketQueue.Count > 0) { QueItem qpack = ResendOutgoingPacketQueue.Dequeue(); - - //SendQueue.Enqueue(qpack); - // m_log.Debug("[PacketQueue]: ThrottleCheck Sending " + qpack.Incoming.ToString() + " packet " + qpack.Packet.Type.ToString()); Client.ProcessOutPacket(qpack.Packet); TotalThrottle.Add(qpack.Packet.ToBytes().Length); ResendThrottle.Add(qpack.Packet.ToBytes().Length); @@ -287,9 +285,6 @@ namespace OpenSim.Region.ClientStack if (LandThrottle.UnderLimit() && LandOutgoingPacketQueue.Count > 0) { QueItem qpack = LandOutgoingPacketQueue.Dequeue(); - - //SendQueue.Enqueue(qpack); - // m_log.Debug("[PacketQueue]: ThrottleCheck Sending " + qpack.Incoming.ToString() + " packet " + qpack.Packet.Type.ToString()); Client.ProcessOutPacket(qpack.Packet); TotalThrottle.Add(qpack.Packet.ToBytes().Length); LandThrottle.Add(qpack.Packet.ToBytes().Length); @@ -297,9 +292,6 @@ namespace OpenSim.Region.ClientStack if (WindThrottle.UnderLimit() && WindOutgoingPacketQueue.Count > 0) { QueItem qpack = WindOutgoingPacketQueue.Dequeue(); - - // SendQueue.Enqueue(qpack); - //m_log.Debug("[PacketQueue]: ThrottleCheck Sending " + qpack.Incoming.ToString() + " packet " + qpack.Packet.Type.ToString()); Client.ProcessOutPacket(qpack.Packet); TotalThrottle.Add(qpack.Packet.ToBytes().Length); WindThrottle.Add(qpack.Packet.ToBytes().Length); @@ -307,9 +299,6 @@ namespace OpenSim.Region.ClientStack if (CloudThrottle.UnderLimit() && CloudOutgoingPacketQueue.Count > 0) { QueItem qpack = CloudOutgoingPacketQueue.Dequeue(); - - //SendQueue.Enqueue(qpack); - // m_log.Debug("[PacketQueue]: ThrottleCheck Sending " + qpack.Incoming.ToString() + " packet " + qpack.Packet.Type.ToString()); Client.ProcessOutPacket(qpack.Packet); TotalThrottle.Add(qpack.Packet.ToBytes().Length); CloudThrottle.Add(qpack.Packet.ToBytes().Length); @@ -317,20 +306,13 @@ namespace OpenSim.Region.ClientStack if (TaskThrottle.UnderLimit() && TaskOutgoingPacketQueue.Count > 0) { QueItem qpack = TaskOutgoingPacketQueue.Dequeue(); - - //SendQueue.Enqueue(qpack); - // m_log.Debug("[PacketQueue]: ThrottleCheck Sending " + qpack.Incoming.ToString() + " packet " + qpack.Packet.Type.ToString()); Client.ProcessOutPacket(qpack.Packet); - TotalThrottle.Add(qpack.Packet.ToBytes().Length); TaskThrottle.Add(qpack.Packet.ToBytes().Length); } if (TextureThrottle.UnderLimit() && TextureOutgoingPacketQueue.Count > 0) { QueItem qpack = TextureOutgoingPacketQueue.Dequeue(); - - //SendQueue.Enqueue(qpack); - // m_log.Debug("[PacketQueue]: ThrottleCheck Sending " + qpack.Incoming.ToString() + " packet " + qpack.Packet.Type.ToString()); Client.ProcessOutPacket(qpack.Packet); TotalThrottle.Add(qpack.Packet.ToBytes().Length); TextureThrottle.Add(qpack.Packet.ToBytes().Length); @@ -338,9 +320,6 @@ namespace OpenSim.Region.ClientStack if (AssetThrottle.UnderLimit() && AssetOutgoingPacketQueue.Count > 0) { QueItem qpack = AssetOutgoingPacketQueue.Dequeue(); - - //SendQueue.Enqueue(qpack); - // m_log.Debug("[PacketQueue]: ThrottleCheck Sending " + qpack.Incoming.ToString() + " packet " + qpack.Packet.Type.ToString()); Client.ProcessOutPacket(qpack.Packet); TotalThrottle.Add(qpack.Packet.ToBytes().Length); AssetThrottle.Add(qpack.Packet.ToBytes().Length); @@ -512,5 +491,21 @@ namespace OpenSim.Region.ClientStack // effectively wiggling the slider causes things reset ResetCounters(); } + + // See IPullStatsProvider + public string GetStats() + { + return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}", + SendQueue.Count(), + IncomingPacketQueue.Count, + OutgoingPacketQueue.Count, + ResendOutgoingPacketQueue.Count, + LandOutgoingPacketQueue.Count, + WindOutgoingPacketQueue.Count, + CloudOutgoingPacketQueue.Count, + TaskOutgoingPacketQueue.Count, + TextureOutgoingPacketQueue.Count, + AssetOutgoingPacketQueue.Count); + } } } diff --git a/OpenSim/Region/ClientStack/PacketServer.cs b/OpenSim/Region/ClientStack/PacketServer.cs index 1d1f4a747e..610a723731 100644 --- a/OpenSim/Region/ClientStack/PacketServer.cs +++ b/OpenSim/Region/ClientStack/PacketServer.cs @@ -88,9 +88,8 @@ namespace OpenSim.Region.ClientStack public void Enqueue(uint CircuitCode, Packet packet) { - //m_log.Debug("[PacketServer]: Enquing " + packet.Type.ToString() + " from " + CircuitCode.ToString()); - //lock(PacketQueue) - PacketQueue.Enqueue(new QueuePacket(packet, CircuitCode)); + IClientAPI client; + PacketQueue.Enqueue(new QueuePacket(packet, CircuitCode)); } public IScene LocalScene diff --git a/OpenSim/Region/ClientStack/UDPServer.cs b/OpenSim/Region/ClientStack/UDPServer.cs index 0a0cfd4b99..470910f34c 100644 --- a/OpenSim/Region/ClientStack/UDPServer.cs +++ b/OpenSim/Region/ClientStack/UDPServer.cs @@ -388,10 +388,10 @@ namespace OpenSim.Region.ClientStack Server.SendTo(buffer, size, flags, sendto); } - else - { - m_log.Debug("[UDPServer]: Failed to find person to send packet to!"); - } + //else + //{ + // m_log.Debug("[UDPServer]: Failed to find person to send packet to!"); + //} } } diff --git a/OpenSim/Region/Environment/Modules/UserTextureDownloadService.cs b/OpenSim/Region/Environment/Modules/UserTextureDownloadService.cs index bec5d2f2a6..1ac6ad227d 100644 --- a/OpenSim/Region/Environment/Modules/UserTextureDownloadService.cs +++ b/OpenSim/Region/Environment/Modules/UserTextureDownloadService.cs @@ -42,8 +42,8 @@ namespace OpenSim.Region.Environment.Modules /// public class UserTextureDownloadService { - //private static readonly log4net.ILog m_log - // = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly log4net.ILog m_log + = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); /// /// Holds texture senders before they have received the appropriate texture from the asset cache. @@ -138,7 +138,6 @@ namespace OpenSim.Region.Environment.Modules // this texture could not be found // TODO Send packet back to the client telling it not to expect the texture - // The absence of this packet doesn't appear to be causing it a problem right now //m_log.DebugFormat("[USER TEXTURE DOWNLOAD]: Removing download stat for {0}", textureID); m_scene.AddPendingDownloads(-1); @@ -150,7 +149,9 @@ namespace OpenSim.Region.Environment.Modules } else { - throw new Exception("Got a texture with no sender object to handle it, this shouldn't happen"); + m_log.WarnFormat( + "Got a texture uuid {0} with no sender object to handle it, this shouldn't happen", + textureID); } } } diff --git a/OpenSim/Region/Environment/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Environment/Scenes/Scene.PacketHandlers.cs index 7c0cd77f28..c3934795b3 100644 --- a/OpenSim/Region/Environment/Scenes/Scene.PacketHandlers.cs +++ b/OpenSim/Region/Environment/Scenes/Scene.PacketHandlers.cs @@ -104,11 +104,16 @@ namespace OpenSim.Region.Environment.Scenes { if (ent is SceneObjectGroup) { + if (((SceneObjectGroup) ent).LocalId == primLocalID) { - ((SceneObjectGroup) ent).GetProperties(remoteClient); - ((SceneObjectGroup) ent).IsSelected = true; - LandManager.setPrimsTainted(); + // A prim is only tainted if it's allowed to be edited by the person clicking it. + if (m_permissionManager.CanEditObjectPosition(remoteClient.AgentId, ((SceneObjectGroup)ent).UUID) || m_permissionManager.CanEditObject(remoteClient.AgentId, ((SceneObjectGroup)ent).UUID)) + { + ((SceneObjectGroup) ent).GetProperties(remoteClient); + ((SceneObjectGroup) ent).IsSelected = true; + LandManager.setPrimsTainted(); + } break; } } @@ -130,9 +135,12 @@ namespace OpenSim.Region.Environment.Scenes { if (((SceneObjectGroup) ent).LocalId == primLocalID) { - ((SceneObjectGroup) ent).IsSelected = false; - LandManager.setPrimsTainted(); - break; + if (m_permissionManager.CanEditObjectPosition(remoteClient.AgentId, ((SceneObjectGroup)ent).UUID) || m_permissionManager.CanEditObject(remoteClient.AgentId, ((SceneObjectGroup)ent).UUID)) + { + ((SceneObjectGroup) ent).IsSelected = false; + LandManager.setPrimsTainted(); + break; + } } } } diff --git a/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs index 8fd9edb831..801e61408c 100644 --- a/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs @@ -203,6 +203,14 @@ namespace OpenSim.Region.Environment.Scenes if (m_rootPart.PhysActor != null) { m_rootPart.PhysActor.Selected = value; + // Pass it on to the children. + foreach (SceneObjectPart child in Children.Values) + { + if (child.PhysActor != null) + { + child.PhysActor.Selected = value; + } + } } } } diff --git a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsPlugin.cs b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsPlugin.cs index 4e96ce0bfe..e3b9ef341a 100644 --- a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsPlugin.cs +++ b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsPlugin.cs @@ -228,6 +228,11 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin set { return; } } + public override uint LocalID + { + set { return; } + } + public override bool Grabbed { set { return; } diff --git a/OpenSim/Region/Physics/BulletXPlugin/BulletXPlugin.cs b/OpenSim/Region/Physics/BulletXPlugin/BulletXPlugin.cs index 180e14c368..8a4e4f4e52 100644 --- a/OpenSim/Region/Physics/BulletXPlugin/BulletXPlugin.cs +++ b/OpenSim/Region/Physics/BulletXPlugin/BulletXPlugin.cs @@ -895,6 +895,11 @@ namespace OpenSim.Region.Physics.BulletXPlugin set { return; } } + public override uint LocalID + { + set { return; } + } + public override bool Grabbed { set { return; } diff --git a/OpenSim/Region/Physics/Manager/PhysicsActor.cs b/OpenSim/Region/Physics/Manager/PhysicsActor.cs index 7e88479a55..aa6cf43869 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsActor.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsActor.cs @@ -124,6 +124,8 @@ namespace OpenSim.Region.Physics.Manager public abstract PrimitiveBaseShape Shape { set; } + public abstract uint LocalID { set; } + public abstract bool Grabbed { set; } public abstract bool Selected { set; } @@ -228,6 +230,11 @@ namespace OpenSim.Region.Physics.Manager set { return; } } + public override uint LocalID + { + set { return; } + } + public override bool Grabbed { set { return; } diff --git a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs index 499422f3d1..2efca3b278 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs @@ -87,6 +87,8 @@ namespace OpenSim.Region.Physics.OdePlugin private bool m_hackSentFall = false; private bool m_hackSentFly = false; private bool m_foundDebian = false; + public uint m_localID = 0; + private CollisionLocker ode; private string m_name = String.Empty; @@ -94,6 +96,15 @@ namespace OpenSim.Region.Physics.OdePlugin private bool[] m_colliderarr = new bool[11]; private bool[] m_colliderGroundarr = new bool[11]; + // Default we're a Character + private CollisionCategories m_collisionCategories = (CollisionCategories.Character); + + // Default, Collide with Other Geometries, spaces, bodies and characters. + private CollisionCategories m_collisionFlags = (CollisionCategories.Geom + | CollisionCategories.Space + | CollisionCategories.Body + | CollisionCategories.Character + | CollisionCategories.Land); private bool jumping = false; //private float gravityAccel; @@ -157,6 +168,11 @@ namespace OpenSim.Region.Physics.OdePlugin set { m_alwaysRun = value; } } + public override uint LocalID + { + set { m_localID = value; } + } + public override bool Grabbed { set { return; } @@ -404,6 +420,10 @@ namespace OpenSim.Region.Physics.OdePlugin int dAMotorEuler = 1; _parent_scene.waitForSpaceUnlock(_parent_scene.space); Shell = d.CreateCapsule(_parent_scene.space, CAPSULE_RADIUS, CAPSULE_LENGTH); + + d.GeomSetCategoryBits(Shell, (int)m_collisionCategories); + d.GeomSetCollideBits(Shell, (int)m_collisionFlags); + d.MassSetCapsuleTotal(out ShellMass, m_mass, 2, CAPSULE_RADIUS, CAPSULE_LENGTH); Body = d.BodyCreate(_parent_scene.world); d.BodySetPosition(Body, npositionX, npositionY, npositionZ); diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs index 50087a5dac..05dcd7a312 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs @@ -52,12 +52,30 @@ namespace OpenSim.Region.Physics.OdePlugin private PhysicsVector m_taintsize; private PhysicsVector m_taintVelocity = PhysicsVector.Zero; private Quaternion m_taintrot; + private const CollisionCategories m_default_collisionFlags = (CollisionCategories.Geom + | CollisionCategories.Space + | CollisionCategories.Body + | CollisionCategories.Character); private bool m_taintshape = false; private bool m_taintPhysics = false; + private bool m_collidesLand = true; + private bool m_collidesWater = false; + + // Default we're a Geometry + private CollisionCategories m_collisionCategories = (CollisionCategories.Geom ); + + // Default, Collide with Other Geometries, spaces and Bodies + private CollisionCategories m_collisionFlags = m_default_collisionFlags; + public bool m_taintremove = false; public bool m_taintdisable = false; public bool m_disabled = false; public bool m_taintadd = false; + public bool m_taintselected = false; + + + public uint m_localID = 0; + public GCHandle gc; private CollisionLocker ode; @@ -74,6 +92,8 @@ namespace OpenSim.Region.Physics.OdePlugin private bool iscolliding = false; private bool m_isphysical = false; + private bool m_isSelected = false; + private bool m_throttleUpdates = false; private int throttleCounter = 0; public int m_interpenetrationcount = 0; @@ -172,6 +192,11 @@ namespace OpenSim.Region.Physics.OdePlugin set { return; } } + public override uint LocalID + { + set { m_localID = value; } + } + public override bool Grabbed { set { return; } @@ -179,17 +204,59 @@ namespace OpenSim.Region.Physics.OdePlugin public override bool Selected { - set { return; } + set { + // This only makes the object not collidable if the object + // is physical or the object is modified somehow *IN THE FUTURE* + // without this, if an avatar selects prim, they can walk right + // through it while it's selected + + if ((m_isphysical && !_zeroFlag) || !value) + { + m_taintselected = value; + _parent_scene.AddPhysicsActorTaint(this); + } + else + { + + m_taintselected = value; + m_isSelected = value; + } + + } } public void SetGeom(IntPtr geom) { prev_geom = prim_geom; prim_geom = geom; + if (prim_geom != (IntPtr)0) + { + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } //m_log.Warn("Setting Geom to: " + prim_geom); } + public void enableBodySoft() + { + if (m_isphysical) + if (Body != (IntPtr)0) + d.BodyEnable(Body); + + m_disabled = false; + } + + public void disableBodySoft() + { + m_disabled = true; + + if (m_isphysical) + if (Body != (IntPtr)0) + d.BodyDisable(Body); + } + + public void enableBody() { // Sets the geom to a body @@ -204,6 +271,13 @@ namespace OpenSim.Region.Physics.OdePlugin myrot.Z = _orientation.z; d.BodySetQuaternion(Body, ref myrot); d.GeomSetBody(prim_geom, Body); + m_collisionCategories |= CollisionCategories.Body; + m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); + + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + + d.BodySetAutoDisableFlag(Body, true); d.BodySetAutoDisableSteps(Body, 20); @@ -332,6 +406,15 @@ namespace OpenSim.Region.Physics.OdePlugin //this kills the body so things like 'mesh' can re-create it. if (Body != (IntPtr) 0) { + m_collisionCategories &= ~CollisionCategories.Body; + m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land); + + if (prim_geom != (IntPtr)0) + { + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } + _parent_scene.remActivePrim(this); d.BodyDestroy(Body); Body = (IntPtr) 0; @@ -425,10 +508,81 @@ namespace OpenSim.Region.Physics.OdePlugin if (m_taintdisable) changedisable(timestep); + if (m_taintselected != m_isSelected) + changeSelectedStatus(timestep); + if (m_taintVelocity != PhysicsVector.Zero) changevelocity(timestep); } + private void changeSelectedStatus(float timestep) + { + while (ode.lockquery()) + { + } + ode.dlock(_parent_scene.world); + + if (m_taintselected) + { + + + m_collisionCategories = CollisionCategories.Selected; + m_collisionFlags = (CollisionCategories.Sensor | CollisionCategories.Space); + + // We do the body disable soft twice because 'in theory' a collision could have happened + // in between the disabling and the collision properties setting + // which would wake the physical body up from a soft disabling and potentially cause it to fall + // through the ground. + + if (m_isphysical) + { + disableBodySoft(); + } + + if (prim_geom != (IntPtr)0) + { + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } + + if (m_isphysical) + { + disableBodySoft(); + } + + } + else + { + + m_collisionCategories = CollisionCategories.Geom; + + if (m_isphysical) + m_collisionCategories |= CollisionCategories.Body; + + + m_collisionFlags = m_default_collisionFlags; + + if (m_collidesLand) + m_collisionFlags |= CollisionCategories.Land; + if (m_collidesWater) + m_collisionFlags |= CollisionCategories.Water; + + if (prim_geom != (IntPtr)0) + { + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } + if (m_isphysical) + enableBodySoft(); + + + } + + ode.dunlock(_parent_scene.world); + resetCollisionAccounting(); + m_isSelected = m_taintselected; + } + public void ResetTaints() { @@ -438,6 +592,8 @@ namespace OpenSim.Region.Physics.OdePlugin m_taintPhysics = m_isphysical; + m_taintselected = m_isSelected; + m_taintsize = _size; @@ -586,6 +742,9 @@ namespace OpenSim.Region.Physics.OdePlugin ode.dunlock(_parent_scene.world); _parent_scene.geom_name_map[prim_geom] = this.m_primName; _parent_scene.actor_name_map[prim_geom] = (PhysicsActor)this; + + changeSelectedStatus(timestep); + m_taintadd = false; @@ -630,6 +789,8 @@ namespace OpenSim.Region.Physics.OdePlugin } ode.dunlock(_parent_scene.world); + changeSelectedStatus(timestep); + resetCollisionAccounting(); m_taintposition = _position; } @@ -682,7 +843,7 @@ namespace OpenSim.Region.Physics.OdePlugin m_taintdisable = false; } - public void changePhysicsStatus(float timestap) + public void changePhysicsStatus(float timestep) { lock (ode) { @@ -709,6 +870,8 @@ namespace OpenSim.Region.Physics.OdePlugin ode.dunlock(_parent_scene.world); } + changeSelectedStatus(timestep); + resetCollisionAccounting(); m_taintPhysics = m_isphysical; } @@ -880,6 +1043,8 @@ namespace OpenSim.Region.Physics.OdePlugin ode.dunlock(_parent_scene.world); + changeSelectedStatus(timestamp); + resetCollisionAccounting(); m_taintsize = _size; } @@ -927,7 +1092,7 @@ namespace OpenSim.Region.Physics.OdePlugin // Re creates body on size. // EnableBody also does setMass() enableBody(); - d.BodyEnable(Body); + } } else @@ -1032,66 +1197,76 @@ namespace OpenSim.Region.Physics.OdePlugin d.BodyEnable(Body); } } + + _parent_scene.geom_name_map[prim_geom] = oldname; ode.dunlock(_parent_scene.world); + changeSelectedStatus(timestamp); + resetCollisionAccounting(); m_taintshape = false; } public void changeAddForce(float timestamp) { - while (ode.lockquery()) - { - } - ode.dlock(_parent_scene.world); - - - lock (m_forcelist) - { - //m_log.Info("[PHYSICS]: dequeing forcelist"); - if (IsPhysical) - { - PhysicsVector iforce = new PhysicsVector(); - for (int i = 0; i < m_forcelist.Count; i++) - { - iforce = iforce + (m_forcelist[i]*100); - } - d.BodyEnable(Body); - d.BodyAddForce(Body, iforce.X, iforce.Y, iforce.Z); - } - m_forcelist.Clear(); - } - - ode.dunlock(_parent_scene.world); - - m_collisionscore = 0; - m_interpenetrationcount = 0; - m_taintforce = false; - - } - private void changevelocity(float timestep) - { - lock (ode) + if (!m_isSelected) { while (ode.lockquery()) { } ode.dlock(_parent_scene.world); - System.Threading.Thread.Sleep(20); - if (IsPhysical) + + lock (m_forcelist) { - if (Body != (IntPtr)0) + //m_log.Info("[PHYSICS]: dequeing forcelist"); + if (IsPhysical) { - d.BodySetLinearVel(Body, m_taintVelocity.X, m_taintVelocity.Y, m_taintVelocity.Z); + PhysicsVector iforce = new PhysicsVector(); + for (int i = 0; i < m_forcelist.Count; i++) + { + iforce = iforce + (m_forcelist[i] * 100); + } + d.BodyEnable(Body); + d.BodyAddForce(Body, iforce.X, iforce.Y, iforce.Z); } + m_forcelist.Clear(); } ode.dunlock(_parent_scene.world); + + m_collisionscore = 0; + m_interpenetrationcount = 0; + } + m_taintforce = false; + + } + private void changevelocity(float timestep) + { + if (!m_isSelected) + { + lock (ode) + { + while (ode.lockquery()) + { + } + ode.dlock(_parent_scene.world); + + System.Threading.Thread.Sleep(20); + if (IsPhysical) + { + if (Body != (IntPtr)0) + { + d.BodySetLinearVel(Body, m_taintVelocity.X, m_taintVelocity.Y, m_taintVelocity.Z); + } + } + + ode.dunlock(_parent_scene.world); + } + //resetCollisionAccounting(); } - //resetCollisionAccounting(); m_taintVelocity = PhysicsVector.Zero; } public override bool IsPhysical @@ -1272,9 +1447,10 @@ namespace OpenSim.Region.Physics.OdePlugin } } public void UpdatePositionAndVelocity() - { + { // no lock; called from Simulate() -- if you call this from elsewhere, gotta lock or do Monitor.Enter/Exit! PhysicsVector pv = new PhysicsVector(0, 0, 0); + bool lastZeroFlag = _zeroFlag; if (Body != (IntPtr) 0) { d.Vector3 vec = d.BodyGetPosition(Body); @@ -1371,6 +1547,9 @@ namespace OpenSim.Region.Physics.OdePlugin } else { + if (lastZeroFlag != _zeroFlag) + base.RequestPhysicsterseUpdate(); + m_lastVelocity = _velocity; _position = l_position; diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs index b3780fd36d..76bd3f2f76 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs @@ -78,6 +78,21 @@ namespace OpenSim.Region.Physics.OdePlugin } } + [Flags] + public enum CollisionCategories : int + { + Disabled = 0, + Geom = 0x00000001, + Body = 0x00000002, + Space = 0x00000004, + Character = 0x00000008, + Land = 0x00000010, + Water = 0x00000020, + Wind = 0x00000040, + Sensor = 0x00000080, + Selected = 0x00000100 + } + public class OdeScene : PhysicsScene { private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); @@ -997,6 +1012,7 @@ namespace OpenSim.Region.Physics.OdePlugin { // creating a new space for prim and inserting it into main space. staticPrimspace[iprimspaceArrItemX, iprimspaceArrItemY] = d.HashSpaceCreate(IntPtr.Zero); + d.GeomSetCategoryBits(staticPrimspace[iprimspaceArrItemX, iprimspaceArrItemY], (int)CollisionCategories.Space); waitForSpaceUnlock(space); d.SpaceAdd(space, staticPrimspace[iprimspaceArrItemX, iprimspaceArrItemY]); return staticPrimspace[iprimspaceArrItemX, iprimspaceArrItemY]; @@ -1656,6 +1672,12 @@ namespace OpenSim.Region.Physics.OdePlugin offset, thickness, wrap); d.GeomHeightfieldDataSetBounds(HeightmapData, m_regionWidth, m_regionHeight); LandGeom = d.CreateHeightfield(space, HeightmapData, 1); + if (LandGeom != (IntPtr)0) + { + d.GeomSetCategoryBits(LandGeom, (int)(CollisionCategories.Land)); + d.GeomSetCollideBits(LandGeom, (int)(CollisionCategories.Space)); + + } geom_name_map[LandGeom] = "Terrain"; d.Matrix3 R = new d.Matrix3(); diff --git a/OpenSim/Region/Physics/POSPlugin/POSPlugin.cs b/OpenSim/Region/Physics/POSPlugin/POSPlugin.cs index 6d1bec707a..9d70ac8b38 100644 --- a/OpenSim/Region/Physics/POSPlugin/POSPlugin.cs +++ b/OpenSim/Region/Physics/POSPlugin/POSPlugin.cs @@ -357,6 +357,11 @@ namespace OpenSim.Region.Physics.POSPlugin set { return; } } + public override uint LocalID + { + set { return; } + } + public override bool Grabbed { set { return; } @@ -645,6 +650,11 @@ namespace OpenSim.Region.Physics.POSPlugin set { return; } } + public override uint LocalID + { + set { return; } + } + public override bool Grabbed { set { return; } diff --git a/OpenSim/Region/Physics/PhysXPlugin/PhysXPlugin.cs b/OpenSim/Region/Physics/PhysXPlugin/PhysXPlugin.cs index 970f6fb642..83930c3d24 100644 --- a/OpenSim/Region/Physics/PhysXPlugin/PhysXPlugin.cs +++ b/OpenSim/Region/Physics/PhysXPlugin/PhysXPlugin.cs @@ -227,6 +227,11 @@ namespace OpenSim.Region.Physics.PhysXPlugin set { return; } } + public override uint LocalID + { + set { return; } + } + public override bool Grabbed { set { return; } @@ -437,6 +442,11 @@ namespace OpenSim.Region.Physics.PhysXPlugin set { return; } } + public override uint LocalID + { + set { return; } + } + public override bool Grabbed { set { return; } diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AsyncLSLCommandManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AsyncLSLCommandManager.cs index fbdfff87de..33c651102a 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AsyncLSLCommandManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AsyncLSLCommandManager.cs @@ -41,8 +41,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// public class AsyncLSLCommandManager : iScriptEngineFunctionModule { - private Thread cmdHandlerThread; - private int cmdHandlerThreadCycleSleepms; + private static Thread cmdHandlerThread; + private static int cmdHandlerThreadCycleSleepms; private ScriptEngine m_ScriptEngine; @@ -51,18 +51,26 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase m_ScriptEngine = _ScriptEngine; ReadConfig(); - // Start the thread that will be doing the work - cmdHandlerThread = new Thread(CmdHandlerThreadLoop); - cmdHandlerThread.Name = "CmdHandlerThread"; - cmdHandlerThread.Priority = ThreadPriority.BelowNormal; - cmdHandlerThread.IsBackground = true; - cmdHandlerThread.Start(); - OpenSim.Framework.ThreadTracker.Add(cmdHandlerThread); + StartThread(); + } + + private void StartThread() + { + if (cmdHandlerThread == null) + { + // Start the thread that will be doing the work + cmdHandlerThread = new Thread(CmdHandlerThreadLoop); + cmdHandlerThread.Name = "AsyncLSLCmdHandlerThread"; + cmdHandlerThread.Priority = ThreadPriority.BelowNormal; + cmdHandlerThread.IsBackground = true; + cmdHandlerThread.Start(); + OpenSim.Framework.ThreadTracker.Add(cmdHandlerThread); + } } public void ReadConfig() { - cmdHandlerThreadCycleSleepms = m_ScriptEngine.ScriptConfigSource.GetInt("AsyncLLCommandLoopms", 50); + cmdHandlerThreadCycleSleepms = m_ScriptEngine.ScriptConfigSource.GetInt("AsyncLLCommandLoopms", 100); } ~AsyncLSLCommandManager() @@ -84,33 +92,49 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase } } - private void CmdHandlerThreadLoop() + private static void CmdHandlerThreadLoop() { while (true) { - // Check timers - CheckTimerEvents(); - Thread.Sleep(25); - // Check HttpRequests - CheckHttpRequests(); - Thread.Sleep(25); - // Check XMLRPCRequests - CheckXMLRPCRequests(); - Thread.Sleep(25); - // Check Listeners - CheckListeners(); - Thread.Sleep(25); - - // Sleep before next cycle - //Thread.Sleep(cmdHandlerThreadCycleSleepms); + try + { + while (true) + { + Thread.Sleep(cmdHandlerThreadCycleSleepms); + //lock (ScriptEngine.ScriptEngines) + //{ + foreach (ScriptEngine se in new ArrayList(ScriptEngine.ScriptEngines)) + { + se.m_ASYNCLSLCommandManager.DoOneCmdHandlerPass(); + } + //} + // Sleep before next cycle + //Thread.Sleep(cmdHandlerThreadCycleSleepms); + } + } + catch + { + } } } + internal void DoOneCmdHandlerPass() + { + // Check timers + CheckTimerEvents(); + // Check HttpRequests + CheckHttpRequests(); + // Check XMLRPCRequests + CheckXMLRPCRequests(); + // Check Listeners + CheckListeners(); + } + /// /// Remove a specific script (and all its pending commands) /// - /// - /// + /// + /// public void RemoveScript(uint localID, LLUUID itemID) { // Remove a specific script diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs index aa7e3e91c3..825686b6ea 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs @@ -45,7 +45,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public class EventQueueManager : iScriptEngineFunctionModule { // - // Class is instanced in "ScriptEngine" and used by "EventManager" also instanced in "ScriptEngine". + // Class is instanced in "ScriptEngine" and used by "EventManager" which is also instanced in "ScriptEngine". // // Class purpose is to queue and execute functions that are received by "EventManager": // - allowing "EventManager" to release its event thread immediately, thus not interrupting server execution. @@ -68,25 +68,25 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// List of threads (classes) processing event queue /// Note that this may or may not be a reference to a static object depending on PrivateRegionThreads config setting. /// - internal List eventQueueThreads; // Thread pool that we work on + internal static List eventQueueThreads = new List(); // Thread pool that we work on /// /// Locking access to eventQueueThreads AND staticGlobalEventQueueThreads. /// // private object eventQueueThreadsLock = new object(); // Static objects for referencing the objects above if we don't have private threads: - internal static List staticEventQueueThreads; // A static reference used if we don't use private threads + //internal static List staticEventQueueThreads; // A static reference used if we don't use private threads // internal static object staticEventQueueThreadsLock; // Statick lock object reference for same reason /// /// Global static list of all threads (classes) processing event queue -- used by max enforcment thread /// - private List staticGlobalEventQueueThreads = new List(); + //private List staticGlobalEventQueueThreads = new List(); /// /// Used internally to specify how many threads should exit gracefully /// - public int ThreadsToExit; - public object ThreadsToExitLock = new object(); + public static int ThreadsToExit; + public static object ThreadsToExitLock = new object(); //public object queueLock = new object(); // Mutex lock object @@ -94,14 +94,14 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// /// How many threads to process queue with /// - internal int numberOfThreads; + internal static int numberOfThreads; internal static int EventExecutionMaxQueueSize; /// /// Maximum time one function can use for execution before we perform a thread kill. /// - private int maxFunctionExecutionTimems + private static int maxFunctionExecutionTimems { get { return (int)(maxFunctionExecutionTimens / 10000); } set { maxFunctionExecutionTimens = value * 10000; } @@ -111,15 +111,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// Contains nanoseconds version of maxFunctionExecutionTimems so that it matches time calculations better (performance reasons). /// WARNING! ONLY UPDATE maxFunctionExecutionTimems, NEVER THIS DIRECTLY. /// - public long maxFunctionExecutionTimens; + public static long maxFunctionExecutionTimens; /// /// Enforce max execution time /// - public bool EnforceMaxExecutionTime; + public static bool EnforceMaxExecutionTime; /// /// Kill script (unload) when it exceeds execution time /// - private bool KillScriptOnMaxFunctionExecutionTime; + private static bool KillScriptOnMaxFunctionExecutionTime; /// /// List of localID locks for mutex processing of script events @@ -131,7 +131,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// Queue containing events waiting to be executed /// public Queue eventQueue = new Queue(); - + #region " Queue structures " /// /// Queue item structure @@ -172,33 +172,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { m_ScriptEngine = _ScriptEngine; - // TODO: We need to move from single EventQueueManager to list of it in to share threads - bool PrivateRegionThreads = true; // m_ScriptEngine.ScriptConfigSource.GetBoolean("PrivateRegionThreads", false); - - // Create thread pool list and lock object - // Determine from config if threads should be dedicated to regions or shared - if (PrivateRegionThreads) - { - // PRIVATE THREAD POOL PER REGION - eventQueueThreads = new List(); - // eventQueueThreadsLock = new object(); - } - else - { - // SHARED THREAD POOL - // Crate the static objects - if (staticEventQueueThreads == null) - staticEventQueueThreads = new List(); - // if (staticEventQueueThreadsLock == null) - // staticEventQueueThreadsLock = new object(); - - // Now reference our locals to them - eventQueueThreads = staticEventQueueThreads; - //eventQueueThreadsLock = staticEventQueueThreadsLock; - } - ReadConfig(); - + AdjustNumberOfScriptThreads(); } public void ReadConfig() @@ -230,18 +205,18 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase private void Stop() { - if (eventQueueThreads != null && eventQueueThreads != null) + if (eventQueueThreads != null) { // Kill worker threads - //lock (eventQueueThreads) - //{ + lock (eventQueueThreads) + { foreach (EventQueueThreadClass EventQueueThread in new ArrayList(eventQueueThreads)) { AbortThreadClass(EventQueueThread); } //eventQueueThreads.Clear(); //staticGlobalEventQueueThreads.Clear(); - //} + } } // Remove all entries from our event queue @@ -256,9 +231,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase #region " Start / stop script execution threads (ThreadClasses) " private void StartNewThreadClass() { - EventQueueThreadClass eqtc = new EventQueueThreadClass(this); + EventQueueThreadClass eqtc = new EventQueueThreadClass(); eventQueueThreads.Add(eqtc); - staticGlobalEventQueueThreads.Add(eqtc); m_ScriptEngine.Log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: Started new script execution thread. Current thread count: " + eventQueueThreads.Count); } @@ -266,8 +240,6 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { if (eventQueueThreads.Contains(threadClass)) eventQueueThreads.Remove(threadClass); - if (staticGlobalEventQueueThreads.Contains(threadClass)) - staticGlobalEventQueueThreads.Remove(threadClass); try { @@ -426,7 +398,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase // Iterate through all ScriptThreadClasses and check how long their current function has been executing lock (eventQueueThreads) { - foreach (EventQueueThreadClass EventQueueThread in staticGlobalEventQueueThreads) + foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads) { // Is thread currently executing anything? if (EventQueueThread.InExecution) @@ -452,14 +424,14 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase } #endregion - /// - /// If set to true then threads and stuff should try to make a graceful exit - /// - public bool PleaseShutdown - { - get { return _PleaseShutdown; } - set { _PleaseShutdown = value; } - } - private bool _PleaseShutdown = false; + ///// + ///// If set to true then threads and stuff should try to make a graceful exit + ///// + //public bool PleaseShutdown + //{ + // get { return _PleaseShutdown; } + // set { _PleaseShutdown = value; } + //} + //private bool _PleaseShutdown = false; } } diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs index 11fd89648c..3a237da3d7 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs @@ -27,6 +27,7 @@ */ using System; +using System.Collections; using System.Collections.Generic; using System.Text; using System.Threading; @@ -40,27 +41,27 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// /// Because every thread needs some data set for it (time started to execute current function), it will do its work within a class /// - public class EventQueueThreadClass: iScriptEngineFunctionModule + public class EventQueueThreadClass : iScriptEngineFunctionModule { /// /// How many ms to sleep if queue is empty /// - private int nothingToDoSleepms;// = 50; - private ThreadPriority MyThreadPriority; + private static int nothingToDoSleepms;// = 50; + private static ThreadPriority MyThreadPriority; public long LastExecutionStarted; public bool InExecution = false; public bool KillCurrentScript = false; - private EventQueueManager eventQueueManager; + //private EventQueueManager eventQueueManager; public Thread EventQueueThread; private static int ThreadCount = 0; private string ScriptEngineName = "ScriptEngine.Common"; - public EventQueueThreadClass(EventQueueManager eqm) + public EventQueueThreadClass()//EventQueueManager eqm { - eventQueueManager = eqm; + //eventQueueManager = eqm; ReadConfig(); Start(); } @@ -72,34 +73,40 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public void ReadConfig() { - ScriptEngineName = eventQueueManager.m_ScriptEngine.ScriptEngineName; - nothingToDoSleepms = eventQueueManager.m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50); - - // Later with ScriptServer we might want to ask OS for stuff too, so doing this a bit manually - string pri = eventQueueManager.m_ScriptEngine.ScriptConfigSource.GetString("ScriptThreadPriority", "BelowNormal"); - switch (pri.ToLower()) + lock (ScriptEngine.ScriptEngines) { - case "lowest": - MyThreadPriority = ThreadPriority.Lowest; - break; - case "belownormal": - MyThreadPriority = ThreadPriority.BelowNormal; - break; - case "normal": - MyThreadPriority = ThreadPriority.Normal; - break; - case "abovenormal": - MyThreadPriority = ThreadPriority.AboveNormal; - break; - case "highest": - MyThreadPriority = ThreadPriority.Highest; - break; - default: - MyThreadPriority = ThreadPriority.BelowNormal; // Default - eventQueueManager.m_ScriptEngine.Log.Error("[ScriptEngineBase]: Unknown priority type \"" + pri + "\" in config file. Defaulting to \"BelowNormal\"."); - break; - } + foreach (ScriptEngine m_ScriptEngine in ScriptEngine.ScriptEngines) + { + ScriptEngineName = m_ScriptEngine.ScriptEngineName; + nothingToDoSleepms = m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50); + // Later with ScriptServer we might want to ask OS for stuff too, so doing this a bit manually + string pri = m_ScriptEngine.ScriptConfigSource.GetString("ScriptThreadPriority", "BelowNormal"); + switch (pri.ToLower()) + { + case "lowest": + MyThreadPriority = ThreadPriority.Lowest; + break; + case "belownormal": + MyThreadPriority = ThreadPriority.BelowNormal; + break; + case "normal": + MyThreadPriority = ThreadPriority.Normal; + break; + case "abovenormal": + MyThreadPriority = ThreadPriority.AboveNormal; + break; + case "highest": + MyThreadPriority = ThreadPriority.Highest; + break; + default: + MyThreadPriority = ThreadPriority.BelowNormal; // Default + m_ScriptEngine.Log.Error("[ScriptEngineBase]: Unknown priority type \"" + pri + + "\" in config file. Defaulting to \"BelowNormal\"."); + break; + } + } + } // Now set that priority if (EventQueueThread != null) if (EventQueueThread.IsAlive) @@ -113,7 +120,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { EventQueueThread = new Thread(EventQueueThreadLoop); EventQueueThread.IsBackground = true; - + EventQueueThread.Priority = MyThreadPriority; EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; EventQueueThread.Start(); @@ -127,8 +134,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public void Stop() { - PleaseShutdown = true; // Set shutdown flag - Thread.Sleep(100); // Wait a bit + //PleaseShutdown = true; // Set shutdown flag + //Thread.Sleep(100); // Wait a bit if (EventQueueThread != null && EventQueueThread.IsAlive == true) { try @@ -143,6 +150,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase } } + private EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct(); + private ScriptEngine lastScriptEngine; /// /// Queue processing thread loop /// @@ -151,171 +160,24 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread spawned"); try { - while (true) + while (true) { try { - EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct(); while (true) { - // Every now and then check if we should shut down - if (PleaseShutdown || eventQueueManager.ThreadsToExit > 0) - { - // Someone should shut down, lets get exclusive lock - lock (eventQueueManager.ThreadsToExitLock) - { - // Lets re-check in case someone grabbed it - if (eventQueueManager.ThreadsToExit > 0) - { - // Its crowded here so we'll shut down - eventQueueManager.ThreadsToExit--; - Stop(); - return; - } - else - { - // We have been asked to shut down - Stop(); - return; - } - } - } - - //try - // { - EventQueueManager.QueueItemStruct QIS = BlankQIS; - bool GotItem = false; - - if (PleaseShutdown) - return; - - if (eventQueueManager.eventQueue.Count == 0) - { - // Nothing to do? Sleep a bit waiting for something to do - Thread.Sleep(nothingToDoSleepms); - } - else - { - // Something in queue, process - //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName); - - // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD - lock (eventQueueManager.eventQueue) - { - GotItem = false; - for (int qc = 0; qc < eventQueueManager.eventQueue.Count; qc++) - { - // Get queue item - QIS = eventQueueManager.eventQueue.Dequeue(); - - // Check if object is being processed by someone else - if (eventQueueManager.TryLock(QIS.localID) == false) - { - // Object is already being processed, requeue it - eventQueueManager.eventQueue.Enqueue(QIS); - } - else - { - // We have lock on an object and can process it - GotItem = true; - break; - } - } - } - - if (GotItem == true) - { - // Execute function - try - { -///cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined -#if DEBUG - //eventQueueManager.m_ScriptEngine.Log.Debug("[" + ScriptEngineName + "]: " + - // "Executing event:\r\n" - // + "QIS.localID: " + QIS.localID - // + ", QIS.itemID: " + QIS.itemID - // + ", QIS.functionName: " + - // QIS.functionName); -#endif - LastExecutionStarted = DateTime.Now.Ticks; - KillCurrentScript = false; - InExecution = true; - eventQueueManager.m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, - QIS.itemID, - QIS.functionName, - QIS.llDetectParams, - QIS.param); - InExecution = false; - } - catch (Exception e) - { - InExecution = false; - // DISPLAY ERROR INWORLD - string text = "Error executing script function \"" + QIS.functionName + - "\":\r\n"; - if (e.InnerException != null) - { - // Send inner exception - text += e.InnerException.Message.ToString(); - } - else - { - text += "\r\n"; - // Send normal - text += e.Message.ToString(); - } - if (KillCurrentScript) - text += "\r\nScript will be deactivated!"; - - try - { - if (text.Length > 1500) - text = text.Substring(0, 1500); - IScriptHost m_host = - eventQueueManager.m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); - //if (m_host != null) - //{ - eventQueueManager.m_ScriptEngine.World.SimChat(Helpers.StringToField(text), - ChatTypeEnum.Say, 0, - m_host.AbsolutePosition, - m_host.Name, m_host.UUID); - } - catch - { - //} - //else - //{ - // T oconsole - eventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName + "]: " + - "Unable to send text in-world:\r\n" + - text); - } - finally - { - // So we are done sending message in-world - if (KillCurrentScript) - { - eventQueueManager.m_ScriptEngine.m_ScriptManager.StopScript( - QIS.localID, QIS.itemID); - } - } - } - finally - { - InExecution = false; - eventQueueManager.ReleaseLock(QIS.localID); - } - } - } + DoProcessQueue(); } } catch (ThreadAbortException tae) { - eventQueueManager.m_ScriptEngine.Log.Info("[" + ScriptEngineName + "]: ThreadAbortException while executing function."); + if (lastScriptEngine != null) + lastScriptEngine.Log.Info("[" + ScriptEngineName + "]: ThreadAbortException while executing function."); } catch (Exception e) { - eventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName + "]: Exception in EventQueueThreadLoop: " + e.ToString()); + if (lastScriptEngine != null) + lastScriptEngine.Log.Error("[" + ScriptEngineName + "]: Exception in EventQueueThreadLoop: " + e.ToString()); } } } @@ -325,14 +187,178 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase } } - /// - /// If set to true then threads and stuff should try to make a graceful exit - /// - public bool PleaseShutdown + public void DoProcessQueue() { - get { return _PleaseShutdown; } - set { _PleaseShutdown = value; } + //lock (ScriptEngine.ScriptEngines) + //{ + foreach (ScriptEngine m_ScriptEngine in new ArrayList(ScriptEngine.ScriptEngines)) + { + lastScriptEngine = m_ScriptEngine; + // Every now and then check if we should shut down + //if (PleaseShutdown || EventQueueManager.ThreadsToExit > 0) + //{ + // // Someone should shut down, lets get exclusive lock + // lock (EventQueueManager.ThreadsToExitLock) + // { + // // Lets re-check in case someone grabbed it + // if (EventQueueManager.ThreadsToExit > 0) + // { + // // Its crowded here so we'll shut down + // EventQueueManager.ThreadsToExit--; + // Stop(); + // return; + // } + // else + // { + // // We have been asked to shut down + // Stop(); + // return; + // } + // } + //} + + //try + // { + EventQueueManager.QueueItemStruct QIS = BlankQIS; + bool GotItem = false; + + //if (PleaseShutdown) + // return; + + if (m_ScriptEngine.m_EventQueueManager == null || m_ScriptEngine.m_EventQueueManager.eventQueue == null) + continue; + + if (m_ScriptEngine.m_EventQueueManager.eventQueue.Count == 0) + { + // Nothing to do? Sleep a bit waiting for something to do + Thread.Sleep(nothingToDoSleepms); + } + else + { + // Something in queue, process + //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName); + + // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD + lock (m_ScriptEngine.m_EventQueueManager.eventQueue) + { + GotItem = false; + for (int qc = 0; qc < m_ScriptEngine.m_EventQueueManager.eventQueue.Count; qc++) + { + // Get queue item + QIS = m_ScriptEngine.m_EventQueueManager.eventQueue.Dequeue(); + + // Check if object is being processed by someone else + if (m_ScriptEngine.m_EventQueueManager.TryLock(QIS.localID) == false) + { + // Object is already being processed, requeue it + m_ScriptEngine.m_EventQueueManager.eventQueue.Enqueue(QIS); + } + else + { + // We have lock on an object and can process it + GotItem = true; + break; + } + } + } + + if (GotItem == true) + { + // Execute function + try + { + ///cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined +#if DEBUG + //eventQueueManager.m_ScriptEngine.Log.Debug("[" + ScriptEngineName + "]: " + + // "Executing event:\r\n" + // + "QIS.localID: " + QIS.localID + // + ", QIS.itemID: " + QIS.itemID + // + ", QIS.functionName: " + + // QIS.functionName); +#endif + LastExecutionStarted = DateTime.Now.Ticks; + KillCurrentScript = false; + InExecution = true; + m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, + QIS.itemID, + QIS.functionName, + QIS.llDetectParams, + QIS.param); + InExecution = false; + } + catch (Exception e) + { + InExecution = false; + // DISPLAY ERROR INWORLD + string text = "Error executing script function \"" + QIS.functionName + + "\":\r\n"; + if (e.InnerException != null) + { + // Send inner exception + text += e.InnerException.Message.ToString(); + } + else + { + text += "\r\n"; + // Send normal + text += e.Message.ToString(); + } + if (KillCurrentScript) + text += "\r\nScript will be deactivated!"; + + try + { + if (text.Length > 1500) + text = text.Substring(0, 1500); + IScriptHost m_host = + m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); + //if (m_host != null) + //{ + m_ScriptEngine.World.SimChat(Helpers.StringToField(text), + ChatTypeEnum.Say, 0, + m_host.AbsolutePosition, + m_host.Name, m_host.UUID); + } + catch + { + //} + //else + //{ + // T oconsole + m_ScriptEngine.m_EventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName + + "]: " + + "Unable to send text in-world:\r\n" + + text); + } + finally + { + // So we are done sending message in-world + if (KillCurrentScript) + { + m_ScriptEngine.m_EventQueueManager.m_ScriptEngine.m_ScriptManager.StopScript( + QIS.localID, QIS.itemID); + } + } + } + finally + { + InExecution = false; + m_ScriptEngine.m_EventQueueManager.ReleaseLock(QIS.localID); + } + } + } + } + // } } - private bool _PleaseShutdown = false; + + ///// + ///// If set to true then threads and stuff should try to make a graceful exit + ///// + //public bool PleaseShutdown + //{ + // get { return _PleaseShutdown; } + // set { _PleaseShutdown = value; } + //} + //private bool _PleaseShutdown = false; } } diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs index f959a5b412..0fcabab723 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs @@ -27,6 +27,7 @@ */ using System; +using System.Collections; using System.Collections.Generic; using System.Text; using System.Threading; @@ -38,12 +39,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// public class MaintenanceThread : iScriptEngineFunctionModule { - public ScriptEngine m_ScriptEngine; + //public ScriptEngine m_ScriptEngine; private int MaintenanceLoopms; + private int MaintenanceLoopTicks_ScriptLoadUnload; + private int MaintenanceLoopTicks_Other; - public MaintenanceThread(ScriptEngine _ScriptEngine) + + public MaintenanceThread() { - m_ScriptEngine = _ScriptEngine; + //m_ScriptEngine = _ScriptEngine; ReadConfig(); @@ -58,7 +62,20 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public void ReadConfig() { - MaintenanceLoopms = m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopms", 50); + // Bad hack, but we need a m_ScriptEngine :) + lock (ScriptEngine.ScriptEngines) + { + foreach (ScriptEngine m_ScriptEngine in ScriptEngine.ScriptEngines) + { + MaintenanceLoopms = m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopms", 50); + MaintenanceLoopTicks_ScriptLoadUnload = + m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopTicks_ScriptLoadUnload", 1); + MaintenanceLoopTicks_Other = + m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopTicks_Other", 10); + + return; + } + } } #region " Maintenance thread " @@ -90,14 +107,14 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase #if DEBUG //m_ScriptEngine.Log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: StopMaintenanceThread() called"); #endif - PleaseShutdown = true; + //PleaseShutdown = true; Thread.Sleep(100); try { if (MaintenanceThreadThread != null && MaintenanceThreadThread.IsAlive) - { - MaintenanceThreadThread.Abort(); - } + { + MaintenanceThreadThread.Abort(); + } } catch (Exception ex) { @@ -105,17 +122,23 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase } } + private ScriptEngine lastScriptEngine; // Keep track of what ScriptEngine instance we are at so we can give exception /// /// A thread should run in this loop and check all running scripts /// public void MaintenanceLoop() { - if (m_ScriptEngine.m_EventQueueManager.maxFunctionExecutionTimens < MaintenanceLoopms) - m_ScriptEngine.Log.Warn("[" + m_ScriptEngine.ScriptEngineName + "]: " + - "Configuration error: MaxEventExecutionTimeMs is less than MaintenanceLoopms. The Maintenance Loop will only check scripts once per run."); + //if (m_ScriptEngine.m_EventQueueManager.maxFunctionExecutionTimens < MaintenanceLoopms) + // m_ScriptEngine.Log.Warn("[" + m_ScriptEngine.ScriptEngineName + "]: " + + // "Configuration error: MaxEventExecutionTimeMs is less than MaintenanceLoopms. The Maintenance Loop will only check scripts once per run."); long Last_maxFunctionExecutionTimens = 0; // DateTime.Now.Ticks; long Last_ReReadConfigFilens = DateTime.Now.Ticks; + int MaintenanceLoopTicks_ScriptLoadUnload_Count = 0; + int MaintenanceLoopTicks_Other_Count = 0; + bool MaintenanceLoopTicks_ScriptLoadUnload_ResetCount = false; + bool MaintenanceLoopTicks_Other_ResetCount = false; + while (true) { try @@ -123,60 +146,93 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase while (true) { System.Threading.Thread.Sleep(MaintenanceLoopms); // Sleep before next pass - if (PleaseShutdown) - return; - if (m_ScriptEngine != null) + // Reset counters? + if (MaintenanceLoopTicks_ScriptLoadUnload_ResetCount) { - // Re-reading config every x seconds - if (m_ScriptEngine.RefreshConfigFilens > 0) - { - // Check if its time to re-read config - if (DateTime.Now.Ticks - Last_ReReadConfigFilens > m_ScriptEngine.RefreshConfigFilens) - { - //Console.WriteLine("Time passed: " + (DateTime.Now.Ticks - Last_ReReadConfigFilens) + ">" + m_ScriptEngine.RefreshConfigFilens ); - // Its time to re-read config file - m_ScriptEngine.ReadConfig(); - Last_ReReadConfigFilens = DateTime.Now.Ticks; // Reset time - } - } - - // Adjust number of running script threads if not correct - if (m_ScriptEngine.m_EventQueueManager != null) - m_ScriptEngine.m_EventQueueManager.AdjustNumberOfScriptThreads(); - - // Check if any script has exceeded its max execution time - if (m_ScriptEngine.m_EventQueueManager != null && m_ScriptEngine.m_EventQueueManager.EnforceMaxExecutionTime) - { - // We are enforcing execution time - if (DateTime.Now.Ticks - Last_maxFunctionExecutionTimens > - m_ScriptEngine.m_EventQueueManager.maxFunctionExecutionTimens) - { - // Its time to check again - m_ScriptEngine.m_EventQueueManager.CheckScriptMaxExecTime(); // Do check - Last_maxFunctionExecutionTimens = DateTime.Now.Ticks; // Reset time - } - } + MaintenanceLoopTicks_ScriptLoadUnload_ResetCount = false; + MaintenanceLoopTicks_ScriptLoadUnload_Count = 0; } + if (MaintenanceLoopTicks_Other_ResetCount) + { + MaintenanceLoopTicks_Other_ResetCount = false; + MaintenanceLoopTicks_Other_Count = 0; + } + + // Increase our counters + MaintenanceLoopTicks_ScriptLoadUnload_Count++; + MaintenanceLoopTicks_Other_Count++; + + + //lock (ScriptEngine.ScriptEngines) + //{ + foreach (ScriptEngine m_ScriptEngine in new ArrayList(ScriptEngine.ScriptEngines)) + { + lastScriptEngine = m_ScriptEngine; + // Re-reading config every x seconds + if (MaintenanceLoopTicks_Other_Count >= MaintenanceLoopTicks_Other) + { + MaintenanceLoopTicks_Other_ResetCount = true; + if (m_ScriptEngine.RefreshConfigFilens > 0) + { + // Check if its time to re-read config + if (DateTime.Now.Ticks - Last_ReReadConfigFilens > + m_ScriptEngine.RefreshConfigFilens) + { + //Console.WriteLine("Time passed: " + (DateTime.Now.Ticks - Last_ReReadConfigFilens) + ">" + m_ScriptEngine.RefreshConfigFilens ); + // Its time to re-read config file + m_ScriptEngine.ReadConfig(); + Last_ReReadConfigFilens = DateTime.Now.Ticks; // Reset time + } + + + // Adjust number of running script threads if not correct + if (m_ScriptEngine.m_EventQueueManager != null) + m_ScriptEngine.m_EventQueueManager.AdjustNumberOfScriptThreads(); + + // Check if any script has exceeded its max execution time + if (EventQueueManager.EnforceMaxExecutionTime) + { + // We are enforcing execution time + if (DateTime.Now.Ticks - Last_maxFunctionExecutionTimens > + EventQueueManager.maxFunctionExecutionTimens) + { + // Its time to check again + m_ScriptEngine.m_EventQueueManager.CheckScriptMaxExecTime(); // Do check + Last_maxFunctionExecutionTimens = DateTime.Now.Ticks; // Reset time + } + } + } + } + if (MaintenanceLoopTicks_ScriptLoadUnload_Count >= MaintenanceLoopTicks_ScriptLoadUnload) + { + MaintenanceLoopTicks_ScriptLoadUnload_ResetCount = true; + // LOAD / UNLOAD SCRIPTS + if (m_ScriptEngine.m_ScriptManager != null) + m_ScriptEngine.m_ScriptManager.DoScriptLoadUnload(); + } + } + //} } } catch (Exception ex) { - m_ScriptEngine.Log.Error("[" + m_ScriptEngine.ScriptEngineName + "]: Exception in MaintenanceLoopThread. Thread will recover after 5 sec throttle. Exception: " + ex.ToString()); + if (lastScriptEngine != null) + lastScriptEngine.Log.Error("[" + lastScriptEngine.ScriptEngineName + "]: Exception in MaintenanceLoopThread. Thread will recover after 5 sec throttle. Exception: " + ex.ToString()); Thread.Sleep(5000); } } } #endregion - /// - /// If set to true then threads and stuff should try to make a graceful exit - /// - public bool PleaseShutdown - { - get { return _PleaseShutdown; } - set { _PleaseShutdown = value; } - } - private bool _PleaseShutdown = false; + ///// + ///// If set to true then threads and stuff should try to make a graceful exit + ///// + //public bool PleaseShutdown + //{ + // get { return _PleaseShutdown; } + // set { _PleaseShutdown = value; } + //} + //private bool _PleaseShutdown = false; } } diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs index 3777b675f4..0d551c6681 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs @@ -27,6 +27,7 @@ */ using System; +using System.Collections.Generic; using System.IO; using Nini.Config; using OpenSim.Framework.Console; @@ -44,15 +45,16 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase [Serializable] public abstract class ScriptEngine : IRegionModule, OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.ScriptEngine, iScriptEngineFunctionModule { - private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + public static List ScriptEngines = new List(); public Scene World; public EventManager m_EventManager; // Handles and queues incoming events from OpenSim public EventQueueManager m_EventQueueManager; // Executes events, handles script threads public ScriptManager m_ScriptManager; // Load, unload and execute scripts public AppDomainManager m_AppDomainManager; // Handles loading/unloading of scripts into AppDomains public AsyncLSLCommandManager m_ASYNCLSLCommandManager; // Asyncronous LSL commands (commands that returns with an event) - public MaintenanceThread m_MaintenanceThread; // Thread that does different kinds of maintenance, for example refreshing config and killing scripts that has been running too long + public static MaintenanceThread m_MaintenanceThread; // Thread that does different kinds of maintenance, for example refreshing config and killing scripts that has been running too long public IConfigSource ConfigSource; public IConfig ScriptConfigSource; @@ -81,8 +83,11 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public ScriptEngine() { - //Common.SendToDebug("ScriptEngine Object Initialized"); - Common.mySE = this; + Common.mySE = this; // For logging, just need any instance, doesn't matter + lock (ScriptEngines) + { + ScriptEngines.Add(this); // Keep a list of ScriptEngines for shared threads to process all instances + } } public void InitializeEngine(Scene Sceneworld, IConfigSource config, bool HookUpToServer, ScriptManager newScriptManager) @@ -106,7 +111,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase m_ScriptManager = newScriptManager; m_AppDomainManager = new AppDomainManager(this); m_ASYNCLSLCommandManager = new AsyncLSLCommandManager(this); - m_MaintenanceThread = new MaintenanceThread(this); + if (m_MaintenanceThread == null) + m_MaintenanceThread = new MaintenanceThread(); m_log.Info("[" + ScriptEngineName + "]: Reading configuration from config section \"" + ScriptEngineName + "\""); ReadConfig(); @@ -118,6 +124,10 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public void Shutdown() { // We are shutting down + lock (ScriptEngines) + { + ScriptEngines.Remove(this); + } } ScriptServerInterfaces.RemoteEvents ScriptServerInterfaces.ScriptEngine.EventManager() @@ -128,17 +138,10 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public void ReadConfig() { #if DEBUG - m_log.Debug("[" + ScriptEngineName + "]: Refreshing configuration for all modules"); + //m_log.Debug("[" + ScriptEngineName + "]: Refreshing configuration for all modules"); #endif RefreshConfigFileSeconds = ScriptConfigSource.GetInt("RefreshConfig", 30); - // Reload from disk? No! - //ConfigSource.Reload(); - //if (File.Exists(OpenSim.Application.iniFilePath)) - //{ - // //ConfigSource.Merge(new IniConfigSource(OpenSim.Application.iniFilePath)); - //} - // Create a new object (probably not necessary?) // ScriptConfigSource = ConfigSource.Configs[ScriptEngineName]; @@ -175,15 +178,6 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase #endregion - /// - /// If set to true then threads and stuff should try to make a graceful exit - /// - public bool PleaseShutdown - { - get { return _PleaseShutdown; } - set { _PleaseShutdown = value; } - } - private bool _PleaseShutdown = false; } } diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs index 684d13385b..7939219834 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs @@ -129,14 +129,14 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase if (PrivateThread) { // Assign one thread per region - scriptLoadUnloadThread = StartScriptLoadUnloadThread(); + //scriptLoadUnloadThread = StartScriptLoadUnloadThread(); } else { // Shared thread - make sure one exist, then assign it to the private if (staticScriptLoadUnloadThread == null) { - staticScriptLoadUnloadThread = StartScriptLoadUnloadThread(); + //staticScriptLoadUnloadThread = StartScriptLoadUnloadThread(); } scriptLoadUnloadThread = staticScriptLoadUnloadThread; } @@ -169,15 +169,12 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase // Abort load/unload thread try { - PleaseShutdown = true; - Thread.Sleep(100); - if (scriptLoadUnloadThread != null) + //PleaseShutdown = true; + //Thread.Sleep(100); + if (scriptLoadUnloadThread != null && scriptLoadUnloadThread.IsAlive == true) { - if (scriptLoadUnloadThread.IsAlive == true) - { - scriptLoadUnloadThread.Abort(); - scriptLoadUnloadThread.Join(); - } + scriptLoadUnloadThread.Abort(); + //scriptLoadUnloadThread.Join(); } } catch @@ -197,23 +194,9 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { if (LUQueue.Count == 0) Thread.Sleep(scriptLoadUnloadThread_IdleSleepms); - if (PleaseShutdown) - return; - if (LUQueue.Count > 0) - { - LUStruct item = LUQueue.Dequeue(); - lock (startStopLock) // Lock so we have only 1 thread working on loading/unloading of scripts - { - if (item.Action == LUType.Unload) - { - _StopScript(item.localID, item.itemID); - } - if (item.Action == LUType.Load) - { - _StartScript(item.localID, item.itemID, item.script); - } - } - } + //if (PleaseShutdown) + // return; + DoScriptLoadUnload(); } } catch (ThreadAbortException tae) @@ -224,6 +207,26 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase } } + public void DoScriptLoadUnload() + { + if (LUQueue.Count > 0) + { + LUStruct item = LUQueue.Dequeue(); + lock (startStopLock) // Lock so we have only 1 thread working on loading/unloading of scripts + { + if (item.Action == LUType.Unload) + { + _StopScript(item.localID, item.itemID); + } + if (item.Action == LUType.Load) + { + _StartScript(item.localID, item.itemID, item.script); + } + } + } + + } + #endregion #region Helper functions @@ -282,7 +285,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public abstract void _StartScript(uint localID, LLUUID itemID, string Script); public abstract void _StopScript(uint localID, LLUUID itemID); - + #endregion @@ -297,10 +300,10 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// Arguments to pass to function internal void ExecuteEvent(uint localID, LLUUID itemID, string FunctionName, EventQueueManager.Queue_llDetectParams_Struct qParams, object[] args) { -//cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined -///#if DEBUG -/// Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName); -///#endif + //cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined + ///#if DEBUG + /// Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName); + ///#endif // Execute a function in the script //m_scriptEngine.Log.Info("[" + ScriptEngineName + "]: Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName); //ScriptBaseInterface Script = (ScriptBaseInterface)GetScript(localID, itemID); @@ -309,10 +312,10 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { return; } -//cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined -///#if DEBUG -/// Console.WriteLine("ScriptEngine: Executing event: " + FunctionName); -///#endif + //cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined + ///#if DEBUG + /// Console.WriteLine("ScriptEngine: Executing event: " + FunctionName); + ///#endif // Must be done in correct AppDomain, so leaving it up to the script itself Script.llDetectParams = qParams; Script.Exec.ExecuteEvent(FunctionName, args); @@ -418,15 +421,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase #endregion - /// - /// If set to true then threads and stuff should try to make a graceful exit - /// - public bool PleaseShutdown - { - get { return _PleaseShutdown; } - set { _PleaseShutdown = value; } - } - private bool _PleaseShutdown = false; + ///// + ///// If set to true then threads and stuff should try to make a graceful exit + ///// + //public bool PleaseShutdown + //{ + // get { return _PleaseShutdown; } + // set { _PleaseShutdown = value; } + //} + //private bool _PleaseShutdown = false; } } diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/iScriptEngineFunctionModule.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/iScriptEngineFunctionModule.cs index 1c730f5756..516d76a723 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/iScriptEngineFunctionModule.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/iScriptEngineFunctionModule.cs @@ -35,6 +35,6 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public interface iScriptEngineFunctionModule { void ReadConfig(); - bool PleaseShutdown { get; set; } +// bool PleaseShutdown { get; set; } } } \ No newline at end of file diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 732530b76b..084d9f7805 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -171,8 +171,8 @@ msgformat = "PRIVMSG {0} : {3} - {1} of {2}" ; Refresh ScriptEngine config options (these settings) every xx seconds ; 0 = Do not refresh ; Set it to number of seconds between refresh, for example 30. -; Will allow you to change ScriptEngine settings while server is running just by editing this file. -; For example to increase or decrease number of threads. +; Will allow you to change ScriptEngine settings while server is running just by using "CONFIG SET" on console +; For example to increase or decrease number of threads: CONFIG SET NumberOfScriptThreads 10 ; NOTE! Disabled for now. Feature does not work. RefreshConfig=0 @@ -184,14 +184,6 @@ NumberOfScriptThreads=2 ; Valid values: Lowest, BelowNormal, Normal, AboveNormal, Highest ScriptThreadPriority=BelowNormal -; Should the script threads be private for each region? -; true: Each region will get dedicated to scripts within that region -; Number of threads will be * -; false: All regions share for all their scripts -; Note! If you run multiple script engines based on "OpenSim.Region.ScriptEngine.Common" then all of them will share the same threads. -; *** This setting will not work until you restart OpenSim -PrivateRegionThreads=false - ; How long MAX should a script event be allowed to run (per event execution)? ; Do not set this too low (like 50ms) as there are some time wasted in simply executing a function ; There is also a small speed penalty for every kill that is made @@ -218,15 +210,24 @@ SleepTimeIfNoScriptExecutionMs=50 ; Each AppDomain has some memory overhead. But leaving dead scripts in memory also has memory overhead. ScriptsPerAppDomain=1 -; Script loading / unloading sleep +; MaintenanceLoop +; How often to run maintenance loop +; Maintenance loop is doing: script compile/load, script unload, reload config, adjust running config and enforce max execution time +MaintenanceLoopms=50 + +; How many maintenanceloops between each of these. +; (if 2 then function will be executed every MaintenanceLoopms*2 ms) +; Script loading/unloading + ; How long load/unload thread should sleep if there is nothing to do ; Higher value makes it respond slower when scripts are added/removed from prims ; But once active it will process all in queue before sleeping again -ScriptLoadUnloadLoopms=30 +MaintenanceLoopTicks_ScriptLoadUnload=1 + +; Other tasks +; check if we need to reload config, adjust running config and enforce max execution time +MaintenanceLoopTicks_Other=10 -; Loading and unloading of scripts is queued and processed by a separate thread. -; This thread can either be shared among all regions, or private (one thread per region) -PrivateScriptLoadUnloadThread=false ; Maximum number of items in load/unload queue before we start rejecting loads ; Note that we will only be rejecting load. Unloads will still be able to queue. diff --git a/bin/startuplogo.txt b/bin/startuplogo.txt index 6749457056..08d0eddcbf 100644 --- a/bin/startuplogo.txt +++ b/bin/startuplogo.txt @@ -1,41 +1 @@ - - ### ### - ####### #### ####### - ############################ - ############################ - ########################### - ######################### - ##################### - ###### ###### ####### - ##### #### ###### - ##### #### ###### - ###### ###### ####### - ###################### - ####################### - ######################### - ########################### - ########################### - ########################### - ########################### - ########################## - ######################### - ####################### - ##################### - ################## - ############# - ### ##### ### ## ### ### - ####### ###### ## ### ### - ### ## ## ## ## #### #### - ## ### ## ### ### ## ### ## ## #### #### - ### ## ####### ##### ###### #### ## #### #### - ### ## ## ## ## # ## ## ###### ## ## # # ## - ### ## ## ## ####### ## ## ### ## ## ### ## - ## ### ## ## ## ## ## ## ## ## ## ### ## - ### ### ### ## ## ## ## ## ### ### ## ## ### ## - ####### ###### ##### ## ## ###### ## ## # ## - ### ## ## ### ### - ## - ## STARTUP COMPLETE - - http://opensimulator.org/wiki/FAQ - +STARTUP COMPLETE \ No newline at end of file diff --git a/prebuild.xml b/prebuild.xml index 9b1a77117c..532dd132c0 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -147,6 +147,7 @@ ../../../bin/ + @@ -380,6 +381,7 @@ + @@ -478,6 +480,7 @@ + @@ -752,6 +755,7 @@ + @@ -1506,36 +1510,6 @@ - - - - ../../../bin/ - - - - - ../../../bin/ - - - - ../../../bin/ - - - - - - - - - - - - - - - - -