From 5745db2a351de75aff71cb666fa50880b9df9113 Mon Sep 17 00:00:00 2001 From: Adam Frisby Date: Sat, 23 Feb 2008 03:30:43 +0000 Subject: [PATCH] * Rex merge, rest of clientstack. Clean merge. --- OpenSim/Region/ClientStack/PacketQueue.cs | 1005 +++++++++-------- OpenSim/Region/ClientStack/PacketServer.cs | 290 ++--- .../ClientStack/RegionApplicationBase.cs | 326 +++--- OpenSim/Region/ClientStack/UDPServer.cs | 734 ++++++------ 4 files changed, 1256 insertions(+), 1099 deletions(-) diff --git a/OpenSim/Region/ClientStack/PacketQueue.cs b/OpenSim/Region/ClientStack/PacketQueue.cs index a06cb10146..4673082699 100644 --- a/OpenSim/Region/ClientStack/PacketQueue.cs +++ b/OpenSim/Region/ClientStack/PacketQueue.cs @@ -1,485 +1,520 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Timers; -using libsecondlife.Packets; -using OpenSim.Framework; -using Timer=System.Timers.Timer; - -namespace OpenSim.Region.ClientStack -{ - public class PacketQueue - { - private bool m_enabled = true; - - private BlockingQueue SendQueue; - - private Queue IncomingPacketQueue; - private Queue OutgoingPacketQueue; - private Queue ResendOutgoingPacketQueue; - private Queue LandOutgoingPacketQueue; - private Queue WindOutgoingPacketQueue; - private Queue CloudOutgoingPacketQueue; - private Queue TaskOutgoingPacketQueue; - private Queue TextureOutgoingPacketQueue; - private Queue AssetOutgoingPacketQueue; - - private Dictionary PendingAcks = new Dictionary(); - private Dictionary NeedAck = new Dictionary(); - - // All throttle times and number of bytes are calculated by dividing by this value - // This value also determines how many times per throttletimems the timer will run - // If throttleimems is 1000 ms, then the timer will fire every 1000/7 milliseconds - - private int throttleTimeDivisor = 7; - - private int throttletimems = 1000; - - private PacketThrottle ResendThrottle; - private PacketThrottle LandThrottle; - private PacketThrottle WindThrottle; - private PacketThrottle CloudThrottle; - private PacketThrottle TaskThrottle; - private PacketThrottle AssetThrottle; - private PacketThrottle TextureThrottle; - private PacketThrottle TotalThrottle; - - // private long LastThrottle; - // private long ThrottleInterval; - private Timer throttleTimer; - - public PacketQueue() - { - // While working on this, the BlockingQueue had me fooled for a bit. - // The Blocking queue causes the thread to stop until there's something - // in it to process. it's an on-purpose threadlock though because - // without it, the clientloop will suck up all sim resources. - - SendQueue = new BlockingQueue(); - - IncomingPacketQueue = new Queue(); - OutgoingPacketQueue = new Queue(); - ResendOutgoingPacketQueue = new Queue(); - LandOutgoingPacketQueue = new Queue(); - WindOutgoingPacketQueue = new Queue(); - CloudOutgoingPacketQueue = new Queue(); - TaskOutgoingPacketQueue = new Queue(); - TextureOutgoingPacketQueue = new Queue(); - AssetOutgoingPacketQueue = new Queue(); - - - // Set up the throttle classes (min, max, current) in bytes - ResendThrottle = new PacketThrottle(5000, 100000, 50000); - LandThrottle = new PacketThrottle(1000, 100000, 100000); - WindThrottle = new PacketThrottle(1000, 100000, 10000); - CloudThrottle = new PacketThrottle(1000, 100000, 50000); - TaskThrottle = new PacketThrottle(1000, 800000, 100000); - AssetThrottle = new PacketThrottle(1000, 800000, 80000); - TextureThrottle = new PacketThrottle(1000, 800000, 100000); - // Total Throttle trumps all - // Number of bytes allowed to go out per second. (256kbps per client) - TotalThrottle = new PacketThrottle(0, 162144, 1536000); - - throttleTimer = new Timer((int) (throttletimems/throttleTimeDivisor)); - throttleTimer.Elapsed += new ElapsedEventHandler(ThrottleTimerElapsed); - throttleTimer.Start(); - - // TIMERS needed for this - // LastThrottle = DateTime.Now.Ticks; - // ThrottleInterval = (long)(throttletimems/throttleTimeDivisor); - } - - /* STANDARD QUEUE MANIPULATION INTERFACES */ - - - public void Enqueue(QueItem item) - { - if (!m_enabled) - { - return; - } - // We could micro lock, but that will tend to actually - // probably be worse than just synchronizing on SendQueue - - lock (this) - { - 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; - - default: - // Acknowledgements and other such stuff should go directly to the blocking Queue - // Throttling them may and likely 'will' be problematic - SendQueue.Enqueue(item); - break; - } - } - } - - public QueItem Dequeue() - { - return SendQueue.Dequeue(); - } - - public void Flush() - { - lock (this) - { - while (PacketsWaiting()) - { - //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up. - if (ResendOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue()); - } - if (LandOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(LandOutgoingPacketQueue.Dequeue()); - } - if (WindOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(WindOutgoingPacketQueue.Dequeue()); - } - if (CloudOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(CloudOutgoingPacketQueue.Dequeue()); - } - if (TaskOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(TaskOutgoingPacketQueue.Dequeue()); - } - if (TextureOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(TextureOutgoingPacketQueue.Dequeue()); - } - if (AssetOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(AssetOutgoingPacketQueue.Dequeue()); - } - } - // MainLog.Instance.Verbose("THROTTLE", "Processed " + throttleLoops + " packets"); - } - } - - public void Close() - { - m_enabled = false; - throttleTimer.Stop(); - } - - private void ResetCounters() - { - ResendThrottle.Reset(); - LandThrottle.Reset(); - WindThrottle.Reset(); - CloudThrottle.Reset(); - TaskThrottle.Reset(); - AssetThrottle.Reset(); - TextureThrottle.Reset(); - TotalThrottle.Reset(); - } - - private bool PacketsWaiting() - { - return (ResendOutgoingPacketQueue.Count > 0 || - LandOutgoingPacketQueue.Count > 0 || - WindOutgoingPacketQueue.Count > 0 || - CloudOutgoingPacketQueue.Count > 0 || - TaskOutgoingPacketQueue.Count > 0 || - AssetOutgoingPacketQueue.Count > 0 || - TextureOutgoingPacketQueue.Count > 0); - } - - public void ProcessThrottle() - { - // I was considering this.. Will an event fire if the thread it's on is blocked? - - // Then I figured out.. it doesn't really matter.. because this thread won't be blocked for long - // The General overhead of the UDP protocol gets sent to the queue un-throttled by this - // so This'll pick up about around the right time. - - int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once. - int throttleLoops = 0; - - // We're going to dequeue all of the saved up packets until - // we've hit the throttle limit or there's no more packets to send - lock (this) - { - ResetCounters(); - // MainLog.Instance.Verbose("THROTTLE", "Entering Throttle"); - while (TotalThrottle.UnderLimit() && PacketsWaiting() && - (throttleLoops <= MaxThrottleLoops)) - { - throttleLoops++; - //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up. - if (ResendThrottle.UnderLimit() && ResendOutgoingPacketQueue.Count > 0) - { - QueItem qpack = ResendOutgoingPacketQueue.Dequeue(); - - SendQueue.Enqueue(qpack); - TotalThrottle.Add(qpack.Packet.ToBytes().Length); - ResendThrottle.Add(qpack.Packet.ToBytes().Length); - } - if (LandThrottle.UnderLimit() && LandOutgoingPacketQueue.Count > 0) - { - QueItem qpack = LandOutgoingPacketQueue.Dequeue(); - - SendQueue.Enqueue(qpack); - TotalThrottle.Add(qpack.Packet.ToBytes().Length); - LandThrottle.Add(qpack.Packet.ToBytes().Length); - } - if (WindThrottle.UnderLimit() && WindOutgoingPacketQueue.Count > 0) - { - QueItem qpack = WindOutgoingPacketQueue.Dequeue(); - - SendQueue.Enqueue(qpack); - TotalThrottle.Add(qpack.Packet.ToBytes().Length); - WindThrottle.Add(qpack.Packet.ToBytes().Length); - } - if (CloudThrottle.UnderLimit() && CloudOutgoingPacketQueue.Count > 0) - { - QueItem qpack = CloudOutgoingPacketQueue.Dequeue(); - - SendQueue.Enqueue(qpack); - TotalThrottle.Add(qpack.Packet.ToBytes().Length); - CloudThrottle.Add(qpack.Packet.ToBytes().Length); - } - if (TaskThrottle.UnderLimit() && TaskOutgoingPacketQueue.Count > 0) - { - QueItem qpack = TaskOutgoingPacketQueue.Dequeue(); - - SendQueue.Enqueue(qpack); - 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); - TotalThrottle.Add(qpack.Packet.ToBytes().Length); - TextureThrottle.Add(qpack.Packet.ToBytes().Length); - } - if (AssetThrottle.UnderLimit() && AssetOutgoingPacketQueue.Count > 0) - { - QueItem qpack = AssetOutgoingPacketQueue.Dequeue(); - - SendQueue.Enqueue(qpack); - TotalThrottle.Add(qpack.Packet.ToBytes().Length); - AssetThrottle.Add(qpack.Packet.ToBytes().Length); - } - } - // MainLog.Instance.Verbose("THROTTLE", "Processed " + throttleLoops + " packets"); - } - } - - private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e) - { - // just to change the signature, and that ProcessThrottle - // will be used elsewhere possibly - ProcessThrottle(); - } - - private void ThrottleCheck(ref PacketThrottle throttle, ref Queue q, QueItem item) - { - // The idea.. is if the packet throttle queues are empty - // and the client is under throttle for the type. Queue - // it up directly. This basically short cuts having to - // wait for the timer to fire to put things into the - // output queue - - if ((q.Count == 0) && (throttle.UnderLimit())) - { - Monitor.Enter(this); - throttle.Add(item.Packet.ToBytes().Length); - TotalThrottle.Add(item.Packet.ToBytes().Length); - SendQueue.Enqueue(item); - Monitor.Pulse(this); - Monitor.Exit(this); - } - else - { - q.Enqueue(item); - } - } - - - private int ScaleThrottle(int value, int curmax, int newmax) - { - return (int) (((float) value/(float) curmax)*newmax); - } - - private byte[] GetThrottlesPacked(float multiplier) - { - int singlefloat = 4; - float tResend = ResendThrottle.Throttle*multiplier; - float tLand = LandThrottle.Throttle*multiplier; - float tWind = WindThrottle.Throttle*multiplier; - float tCloud = CloudThrottle.Throttle*multiplier; - float tTask = TaskThrottle.Throttle*multiplier; - float tTexture = TextureThrottle.Throttle*multiplier; - float tAsset = AssetThrottle.Throttle*multiplier; - - byte[] throttles = new byte[singlefloat*7]; - int i = 0; - Buffer.BlockCopy(BitConverter.GetBytes(tResend), 0, throttles, singlefloat*i, singlefloat); - i++; - Buffer.BlockCopy(BitConverter.GetBytes(tLand), 0, throttles, singlefloat*i, singlefloat); - i++; - Buffer.BlockCopy(BitConverter.GetBytes(tWind), 0, throttles, singlefloat*i, singlefloat); - i++; - Buffer.BlockCopy(BitConverter.GetBytes(tCloud), 0, throttles, singlefloat*i, singlefloat); - i++; - Buffer.BlockCopy(BitConverter.GetBytes(tTask), 0, throttles, singlefloat*i, singlefloat); - i++; - Buffer.BlockCopy(BitConverter.GetBytes(tTexture), 0, throttles, singlefloat*i, singlefloat); - i++; - Buffer.BlockCopy(BitConverter.GetBytes(tAsset), 0, throttles, singlefloat*i, singlefloat); - - return throttles; - } - - public void SetThrottleFromClient(byte[] throttle) - { - int tResend = -1; - int tLand = -1; - int tWind = -1; - int tCloud = -1; - int tTask = -1; - int tTexture = -1; - int tAsset = -1; - int tall = -1; - int singlefloat = 4; - - //Agent Throttle Block contains 7 single floatingpoint values. - int j = 0; - - // Some Systems may be big endian... - // it might be smart to do this check more often... - if (!BitConverter.IsLittleEndian) - for (int i = 0; i < 7; i++) - Array.Reverse(throttle, j + i*singlefloat, singlefloat); - - // values gotten from libsecondlife.org/wiki/Throttle. Thanks MW_ - // bytes - // Convert to integer, since.. the full fp space isn't used. - tResend = (int) BitConverter.ToSingle(throttle, j); - j += singlefloat; - tLand = (int) BitConverter.ToSingle(throttle, j); - j += singlefloat; - tWind = (int) BitConverter.ToSingle(throttle, j); - j += singlefloat; - tCloud = (int) BitConverter.ToSingle(throttle, j); - j += singlefloat; - tTask = (int) BitConverter.ToSingle(throttle, j); - j += singlefloat; - tTexture = (int) BitConverter.ToSingle(throttle, j); - j += singlefloat; - tAsset = (int) BitConverter.ToSingle(throttle, j); - - tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset; - /* - MainLog.Instance.Verbose("CLIENT", "Client AgentThrottle - Got throttle:resendbytes=" + tResend + - " landbytes=" + tLand + - " windbytes=" + tWind + - " cloudbytes=" + tCloud + - " taskbytes=" + tTask + - " texturebytes=" + tTexture + - " Assetbytes=" + tAsset + - " Allbytes=" + tall); - */ - - // Total Sanity - // Make sure that the client sent sane total values. - - // If the client didn't send acceptable values.... - // Scale the clients values down until they are acceptable. - - if (tall <= TotalThrottle.Max) - { - ResendThrottle.Throttle = tResend; - LandThrottle.Throttle = tLand; - WindThrottle.Throttle = tWind; - CloudThrottle.Throttle = tCloud; - TaskThrottle.Throttle = tTask; - TextureThrottle.Throttle = tTexture; - AssetThrottle.Throttle = tAsset; - TotalThrottle.Throttle = tall; - } - else if (tall < 1) - { - // client is stupid, penalize him by minning everything - ResendThrottle.Throttle = ResendThrottle.Min; - LandThrottle.Throttle = LandThrottle.Min; - WindThrottle.Throttle = WindThrottle.Min; - CloudThrottle.Throttle = CloudThrottle.Min; - TaskThrottle.Throttle = TaskThrottle.Min; - TextureThrottle.Throttle = TextureThrottle.Min; - AssetThrottle.Throttle = AssetThrottle.Min; - TotalThrottle.Throttle = TotalThrottle.Min; - } - else - { - // we're over so figure out percentages and use those - ResendThrottle.Throttle = tResend; - - LandThrottle.Throttle = ScaleThrottle(tLand, tall, TotalThrottle.Max); - WindThrottle.Throttle = ScaleThrottle(tWind, tall, TotalThrottle.Max); - CloudThrottle.Throttle = ScaleThrottle(tCloud, tall, TotalThrottle.Max); - TaskThrottle.Throttle = ScaleThrottle(tTask, tall, TotalThrottle.Max); - TextureThrottle.Throttle = ScaleThrottle(tTexture, tall, TotalThrottle.Max); - AssetThrottle.Throttle = ScaleThrottle(tAsset, tall, TotalThrottle.Max); - TotalThrottle.Throttle = TotalThrottle.Max; - } - // effectively wiggling the slider causes things reset - ResetCounters(); - } - } -} \ No newline at end of file +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ +using System; +using System.Collections.Generic; +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 : IPullStatsProvider + { + //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_enabled = true; + + private BlockingQueue SendQueue; + + private Queue IncomingPacketQueue; + private Queue OutgoingPacketQueue; + private Queue ResendOutgoingPacketQueue; + private Queue LandOutgoingPacketQueue; + private Queue WindOutgoingPacketQueue; + private Queue CloudOutgoingPacketQueue; + private Queue TaskOutgoingPacketQueue; + private Queue TextureOutgoingPacketQueue; + private Queue AssetOutgoingPacketQueue; + + private Dictionary PendingAcks = new Dictionary(); + private Dictionary NeedAck = new Dictionary(); + + // All throttle times and number of bytes are calculated by dividing by this value + // This value also determines how many times per throttletimems the timer will run + // If throttleimems is 1000 ms, then the timer will fire every 1000/7 milliseconds + + private int throttleTimeDivisor = 7; + + private int throttletimems = 1000; + + private PacketThrottle ResendThrottle; + private PacketThrottle LandThrottle; + private PacketThrottle WindThrottle; + private PacketThrottle CloudThrottle; + private PacketThrottle TaskThrottle; + private PacketThrottle AssetThrottle; + private PacketThrottle TextureThrottle; + private PacketThrottle TotalThrottle; + + // private long LastThrottle; + // private long ThrottleInterval; + private Timer throttleTimer; + + private LLUUID m_agentId; + + public PacketQueue(LLUUID agentId) + { + // While working on this, the BlockingQueue had me fooled for a bit. + // The Blocking queue causes the thread to stop until there's something + // in it to process. it's an on-purpose threadlock though because + // without it, the clientloop will suck up all sim resources. + + SendQueue = new BlockingQueue(); + + IncomingPacketQueue = new Queue(); + OutgoingPacketQueue = new Queue(); + ResendOutgoingPacketQueue = new Queue(); + LandOutgoingPacketQueue = new Queue(); + WindOutgoingPacketQueue = new Queue(); + CloudOutgoingPacketQueue = new Queue(); + TaskOutgoingPacketQueue = new Queue(); + TextureOutgoingPacketQueue = new Queue(); + AssetOutgoingPacketQueue = new Queue(); + + + // Set up the throttle classes (min, max, current) in bytes + ResendThrottle = new PacketThrottle(5000, 100000, 50000); + LandThrottle = new PacketThrottle(1000, 100000, 100000); + WindThrottle = new PacketThrottle(1000, 100000, 10000); + CloudThrottle = new PacketThrottle(1000, 100000, 50000); + TaskThrottle = new PacketThrottle(1000, 800000, 100000); + AssetThrottle = new PacketThrottle(1000, 800000, 80000); + TextureThrottle = new PacketThrottle(1000, 800000, 100000); + // Total Throttle trumps all + // Number of bytes allowed to go out per second. (256kbps per client) + TotalThrottle = new PacketThrottle(0, 162144, 1536000); + + throttleTimer = new Timer((int) (throttletimems/throttleTimeDivisor)); + throttleTimer.Elapsed += new ElapsedEventHandler(ThrottleTimerElapsed); + throttleTimer.Start(); + + // TIMERS needed for this + // LastThrottle = DateTime.Now.Ticks; + // ThrottleInterval = (long)(throttletimems/throttleTimeDivisor); + + m_agentId = agentId; + + if (StatsManager.SimExtraStats != null) + { + StatsManager.SimExtraStats.RegisterPacketQueueStatsProvider(m_agentId, this); + } + } + + /* STANDARD QUEUE MANIPULATION INTERFACES */ + + + public void Enqueue(QueItem item) + { + if (!m_enabled) + { + return; + } + // We could micro lock, but that will tend to actually + // probably be worse than just synchronizing on SendQueue + + lock (this) + { + 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; + + default: + // Acknowledgements and other such stuff should go directly to the blocking Queue + // Throttling them may and likely 'will' be problematic + SendQueue.Enqueue(item); + break; + } + } + } + + public QueItem Dequeue() + { + return SendQueue.Dequeue(); + } + + public void Flush() + { + lock (this) + { + while (PacketsWaiting()) + { + //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up. + if (ResendOutgoingPacketQueue.Count > 0) + { + SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue()); + } + if (LandOutgoingPacketQueue.Count > 0) + { + SendQueue.Enqueue(LandOutgoingPacketQueue.Dequeue()); + } + if (WindOutgoingPacketQueue.Count > 0) + { + SendQueue.Enqueue(WindOutgoingPacketQueue.Dequeue()); + } + if (CloudOutgoingPacketQueue.Count > 0) + { + SendQueue.Enqueue(CloudOutgoingPacketQueue.Dequeue()); + } + if (TaskOutgoingPacketQueue.Count > 0) + { + SendQueue.Enqueue(TaskOutgoingPacketQueue.Dequeue()); + } + if (TextureOutgoingPacketQueue.Count > 0) + { + SendQueue.Enqueue(TextureOutgoingPacketQueue.Dequeue()); + } + if (AssetOutgoingPacketQueue.Count > 0) + { + SendQueue.Enqueue(AssetOutgoingPacketQueue.Dequeue()); + } + } + // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); + } + } + + public void Close() + { + m_enabled = false; + throttleTimer.Stop(); + + if (StatsManager.SimExtraStats != null) + { + StatsManager.SimExtraStats.DeregisterPacketQueueStatsProvider(m_agentId); + } + } + + private void ResetCounters() + { + ResendThrottle.Reset(); + LandThrottle.Reset(); + WindThrottle.Reset(); + CloudThrottle.Reset(); + TaskThrottle.Reset(); + AssetThrottle.Reset(); + TextureThrottle.Reset(); + TotalThrottle.Reset(); + } + + private bool PacketsWaiting() + { + return (ResendOutgoingPacketQueue.Count > 0 || + LandOutgoingPacketQueue.Count > 0 || + WindOutgoingPacketQueue.Count > 0 || + CloudOutgoingPacketQueue.Count > 0 || + TaskOutgoingPacketQueue.Count > 0 || + AssetOutgoingPacketQueue.Count > 0 || + TextureOutgoingPacketQueue.Count > 0); + } + + public void ProcessThrottle() + { + // I was considering this.. Will an event fire if the thread it's on is blocked? + + // Then I figured out.. it doesn't really matter.. because this thread won't be blocked for long + // The General overhead of the UDP protocol gets sent to the queue un-throttled by this + // so This'll pick up about around the right time. + + int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once. + int throttleLoops = 0; + + // We're going to dequeue all of the saved up packets until + // we've hit the throttle limit or there's no more packets to send + lock (this) + { + ResetCounters(); + // m_log.Info("[THROTTLE]: Entering Throttle"); + while (TotalThrottle.UnderLimit() && PacketsWaiting() && + (throttleLoops <= MaxThrottleLoops)) + { + throttleLoops++; + //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up. + if (ResendThrottle.UnderLimit() && ResendOutgoingPacketQueue.Count > 0) + { + QueItem qpack = ResendOutgoingPacketQueue.Dequeue(); + + SendQueue.Enqueue(qpack); + TotalThrottle.Add(qpack.Packet.ToBytes().Length); + ResendThrottle.Add(qpack.Packet.ToBytes().Length); + } + if (LandThrottle.UnderLimit() && LandOutgoingPacketQueue.Count > 0) + { + QueItem qpack = LandOutgoingPacketQueue.Dequeue(); + + SendQueue.Enqueue(qpack); + TotalThrottle.Add(qpack.Packet.ToBytes().Length); + LandThrottle.Add(qpack.Packet.ToBytes().Length); + } + if (WindThrottle.UnderLimit() && WindOutgoingPacketQueue.Count > 0) + { + QueItem qpack = WindOutgoingPacketQueue.Dequeue(); + + SendQueue.Enqueue(qpack); + TotalThrottle.Add(qpack.Packet.ToBytes().Length); + WindThrottle.Add(qpack.Packet.ToBytes().Length); + } + if (CloudThrottle.UnderLimit() && CloudOutgoingPacketQueue.Count > 0) + { + QueItem qpack = CloudOutgoingPacketQueue.Dequeue(); + + SendQueue.Enqueue(qpack); + TotalThrottle.Add(qpack.Packet.ToBytes().Length); + CloudThrottle.Add(qpack.Packet.ToBytes().Length); + } + if (TaskThrottle.UnderLimit() && TaskOutgoingPacketQueue.Count > 0) + { + QueItem qpack = TaskOutgoingPacketQueue.Dequeue(); + + SendQueue.Enqueue(qpack); + 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); + TotalThrottle.Add(qpack.Packet.ToBytes().Length); + TextureThrottle.Add(qpack.Packet.ToBytes().Length); + } + if (AssetThrottle.UnderLimit() && AssetOutgoingPacketQueue.Count > 0) + { + QueItem qpack = AssetOutgoingPacketQueue.Dequeue(); + + SendQueue.Enqueue(qpack); + TotalThrottle.Add(qpack.Packet.ToBytes().Length); + AssetThrottle.Add(qpack.Packet.ToBytes().Length); + } + } + // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); + } + } + + private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e) + { + // just to change the signature, and that ProcessThrottle + // will be used elsewhere possibly + ProcessThrottle(); + } + + private void ThrottleCheck(ref PacketThrottle throttle, ref Queue q, QueItem item) + { + // The idea.. is if the packet throttle queues are empty + // and the client is under throttle for the type. Queue + // it up directly. This basically short cuts having to + // wait for the timer to fire to put things into the + // output queue + + if ((q.Count == 0) && (throttle.UnderLimit())) + { + Monitor.Enter(this); + throttle.Add(item.Packet.ToBytes().Length); + TotalThrottle.Add(item.Packet.ToBytes().Length); + SendQueue.Enqueue(item); + Monitor.Pulse(this); + Monitor.Exit(this); + } + else + { + q.Enqueue(item); + } + } + + + private int ScaleThrottle(int value, int curmax, int newmax) + { + return (int) (((float) value/(float) curmax)*newmax); + } + + public byte[] GetThrottlesPacked(float multiplier) + { + int singlefloat = 4; + float tResend = ResendThrottle.Throttle*multiplier; + float tLand = LandThrottle.Throttle*multiplier; + float tWind = WindThrottle.Throttle*multiplier; + float tCloud = CloudThrottle.Throttle*multiplier; + float tTask = TaskThrottle.Throttle*multiplier; + float tTexture = TextureThrottle.Throttle*multiplier; + float tAsset = AssetThrottle.Throttle*multiplier; + + byte[] throttles = new byte[singlefloat*7]; + int i = 0; + Buffer.BlockCopy(BitConverter.GetBytes(tResend), 0, throttles, singlefloat*i, singlefloat); + i++; + Buffer.BlockCopy(BitConverter.GetBytes(tLand), 0, throttles, singlefloat*i, singlefloat); + i++; + Buffer.BlockCopy(BitConverter.GetBytes(tWind), 0, throttles, singlefloat*i, singlefloat); + i++; + Buffer.BlockCopy(BitConverter.GetBytes(tCloud), 0, throttles, singlefloat*i, singlefloat); + i++; + Buffer.BlockCopy(BitConverter.GetBytes(tTask), 0, throttles, singlefloat*i, singlefloat); + i++; + Buffer.BlockCopy(BitConverter.GetBytes(tTexture), 0, throttles, singlefloat*i, singlefloat); + i++; + Buffer.BlockCopy(BitConverter.GetBytes(tAsset), 0, throttles, singlefloat*i, singlefloat); + + return throttles; + } + + public void SetThrottleFromClient(byte[] throttle) + { + int tResend = -1; + int tLand = -1; + int tWind = -1; + int tCloud = -1; + int tTask = -1; + int tTexture = -1; + int tAsset = -1; + int tall = -1; + int singlefloat = 4; + + //Agent Throttle Block contains 7 single floatingpoint values. + int j = 0; + + // Some Systems may be big endian... + // it might be smart to do this check more often... + if (!BitConverter.IsLittleEndian) + for (int i = 0; i < 7; i++) + Array.Reverse(throttle, j + i*singlefloat, singlefloat); + + // values gotten from libsecondlife.org/wiki/Throttle. Thanks MW_ + // bytes + // Convert to integer, since.. the full fp space isn't used. + tResend = (int) BitConverter.ToSingle(throttle, j); + j += singlefloat; + tLand = (int) BitConverter.ToSingle(throttle, j); + j += singlefloat; + tWind = (int) BitConverter.ToSingle(throttle, j); + j += singlefloat; + tCloud = (int) BitConverter.ToSingle(throttle, j); + j += singlefloat; + tTask = (int) BitConverter.ToSingle(throttle, j); + j += singlefloat; + tTexture = (int) BitConverter.ToSingle(throttle, j); + j += singlefloat; + tAsset = (int) BitConverter.ToSingle(throttle, j); + + tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset; + /* + m_log.Info("[CLIENT]: Client AgentThrottle - Got throttle:resendbytes=" + tResend + + " landbytes=" + tLand + + " windbytes=" + tWind + + " cloudbytes=" + tCloud + + " taskbytes=" + tTask + + " texturebytes=" + tTexture + + " Assetbytes=" + tAsset + + " Allbytes=" + tall); + */ + + // Total Sanity + // Make sure that the client sent sane total values. + + // If the client didn't send acceptable values.... + // Scale the clients values down until they are acceptable. + + if (tall <= TotalThrottle.Max) + { + ResendThrottle.Throttle = tResend; + LandThrottle.Throttle = tLand; + WindThrottle.Throttle = tWind; + CloudThrottle.Throttle = tCloud; + TaskThrottle.Throttle = tTask; + TextureThrottle.Throttle = tTexture; + AssetThrottle.Throttle = tAsset; + TotalThrottle.Throttle = tall; + } + else if (tall < 1) + { + // client is stupid, penalize him by minning everything + ResendThrottle.Throttle = ResendThrottle.Min; + LandThrottle.Throttle = LandThrottle.Min; + WindThrottle.Throttle = WindThrottle.Min; + CloudThrottle.Throttle = CloudThrottle.Min; + TaskThrottle.Throttle = TaskThrottle.Min; + TextureThrottle.Throttle = TextureThrottle.Min; + AssetThrottle.Throttle = AssetThrottle.Min; + TotalThrottle.Throttle = TotalThrottle.Min; + } + else + { + // we're over so figure out percentages and use those + ResendThrottle.Throttle = tResend; + + LandThrottle.Throttle = ScaleThrottle(tLand, tall, TotalThrottle.Max); + WindThrottle.Throttle = ScaleThrottle(tWind, tall, TotalThrottle.Max); + CloudThrottle.Throttle = ScaleThrottle(tCloud, tall, TotalThrottle.Max); + TaskThrottle.Throttle = ScaleThrottle(tTask, tall, TotalThrottle.Max); + TextureThrottle.Throttle = ScaleThrottle(tTexture, tall, TotalThrottle.Max); + AssetThrottle.Throttle = ScaleThrottle(tAsset, tall, TotalThrottle.Max); + TotalThrottle.Throttle = TotalThrottle.Max; + } + // 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 b24346520a..250b90afcb 100644 --- a/OpenSim/Region/ClientStack/PacketServer.cs +++ b/OpenSim/Region/ClientStack/PacketServer.cs @@ -1,139 +1,151 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ -using System.Net; -using System.Net.Sockets; -using libsecondlife; -using libsecondlife.Packets; -using OpenSim.Framework; -using OpenSim.Framework.Communications.Cache; - -namespace OpenSim.Region.ClientStack -{ - public class PacketServer - { - private ClientStackNetworkHandler m_networkHandler; - private IScene m_scene; - - //private readonly ClientManager m_clientManager = new ClientManager(); - //public ClientManager ClientManager - //{ - // get { return m_clientManager; } - //} - - public PacketServer(ClientStackNetworkHandler networkHandler) - { - m_networkHandler = networkHandler; - m_networkHandler.RegisterPacketServer(this); - } - - public IScene LocalScene - { - set { m_scene = value; } - } - - /// - /// - /// - /// - /// - public virtual void InPacket(uint circuitCode, Packet packet) - { - m_scene.ClientManager.InPacket(circuitCode, packet); - } - - protected virtual IClientAPI CreateNewClient(EndPoint remoteEP, UseCircuitCodePacket initialcirpack, - ClientManager clientManager, IScene scene, AssetCache assetCache, - PacketServer packServer, AgentCircuitManager authenSessions, - LLUUID agentId, LLUUID sessionId, uint circuitCode) - { - return - new ClientView(remoteEP, scene, assetCache, packServer, authenSessions, agentId, sessionId, circuitCode); - } - - public virtual bool AddNewClient(EndPoint epSender, UseCircuitCodePacket useCircuit, AssetCache assetCache, - AgentCircuitManager authenticateSessionsClass) - { - IClientAPI newuser; - - if (m_scene.ClientManager.TryGetClient(useCircuit.CircuitCode.Code, out newuser)) - { - return false; - } - else - { - newuser = CreateNewClient(epSender, useCircuit, m_scene.ClientManager, m_scene, assetCache, this, - authenticateSessionsClass, useCircuit.CircuitCode.ID, - useCircuit.CircuitCode.SessionID, useCircuit.CircuitCode.Code); - - m_scene.ClientManager.Add(useCircuit.CircuitCode.Code, newuser); - - newuser.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler; - newuser.OnLogout += LogoutHandler; - newuser.OnConnectionClosed += CloseClient; - - return true; - } - } - - public void LogoutHandler(IClientAPI client) - { - client.SendLogoutPacket(); - - CloseClient(client); - } - - /// - /// - /// - /// - /// - /// - /// - public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode) - { - m_networkHandler.SendPacketTo(buffer, size, flags, circuitcode); - } - - /// - /// - /// - /// - public virtual void CloseCircuit(uint circuitcode) - { - m_networkHandler.RemoveClientCircuit(circuitcode); - - //m_scene.ClientManager.CloseAllAgents(circuitcode); - } - - public virtual void CloseClient(IClientAPI client) - { - CloseCircuit(client.CircuitCode); - client.Close(false); - } - } -} \ No newline at end of file +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +using System; +using System.Net; +using System.Net.Sockets; +using libsecondlife; +using libsecondlife.Packets; +using OpenSim.Framework; +using OpenSim.Framework.Communications.Cache; + +namespace OpenSim.Region.ClientStack +{ + public class PacketServer + { + //private static readonly log4net.ILog m_log + // = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private ClientStackNetworkHandler m_networkHandler; + private IScene m_scene; + + //private readonly ClientManager m_clientManager = new ClientManager(); + //public ClientManager ClientManager + //{ + // get { return m_clientManager; } + //} + + public PacketServer(ClientStackNetworkHandler networkHandler) + { + m_networkHandler = networkHandler; + m_networkHandler.RegisterPacketServer(this); + } + + public IScene LocalScene + { + set { m_scene = value; } + } + + /// + /// + /// + /// + /// + public virtual void InPacket(uint circuitCode, Packet packet) + { + m_scene.ClientManager.InPacket(circuitCode, packet); + } + + protected virtual IClientAPI CreateNewClient(EndPoint remoteEP, UseCircuitCodePacket initialcirpack, + ClientManager clientManager, IScene scene, AssetCache assetCache, + PacketServer packServer, AgentCircuitManager authenSessions, + LLUUID agentId, LLUUID sessionId, uint circuitCode) + { + return + new ClientView(remoteEP, scene, assetCache, packServer, authenSessions, agentId, sessionId, circuitCode); + } + + public virtual bool AddNewClient(EndPoint epSender, UseCircuitCodePacket useCircuit, AssetCache assetCache, + AgentCircuitManager authenticateSessionsClass) + { + IClientAPI newuser; + + if (m_scene.ClientManager.TryGetClient(useCircuit.CircuitCode.Code, out newuser)) + { + return false; + } + else + { + newuser = CreateNewClient(epSender, useCircuit, m_scene.ClientManager, m_scene, assetCache, this, + authenticateSessionsClass, useCircuit.CircuitCode.ID, + useCircuit.CircuitCode.SessionID, useCircuit.CircuitCode.Code); + + m_scene.ClientManager.Add(useCircuit.CircuitCode.Code, newuser); + + newuser.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler; + newuser.OnLogout += LogoutHandler; + newuser.OnConnectionClosed += CloseClient; + + return true; + } + } + + public void LogoutHandler(IClientAPI client) + { + client.SendLogoutPacket(); + + CloseClient(client); + } + + /// + /// + /// + /// + /// + /// + /// + public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode) + { + m_networkHandler.SendPacketTo(buffer, size, flags, circuitcode); + } + + /// + /// + /// + /// + public virtual void CloseCircuit(uint circuitcode) + { + m_networkHandler.RemoveClientCircuit(circuitcode); + + //m_scene.ClientManager.CloseAllAgents(circuitcode); + } + + /// + /// Completely close down the given client. + /// + /// + public virtual void CloseClient(IClientAPI client) + { + //m_log.Info("PacketServer:CloseClient()"); + + CloseCircuit(client.CircuitCode); + m_scene.ClientManager.Remove(client.CircuitCode); + client.Close(false); + } + } +} diff --git a/OpenSim/Region/ClientStack/RegionApplicationBase.cs b/OpenSim/Region/ClientStack/RegionApplicationBase.cs index 2c94ba0843..3276c855e0 100644 --- a/OpenSim/Region/ClientStack/RegionApplicationBase.cs +++ b/OpenSim/Region/ClientStack/RegionApplicationBase.cs @@ -1,160 +1,166 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ -using System; -using System.Collections.Generic; -using System.Net; -using libsecondlife; -using OpenSim.Framework; -using OpenSim.Framework.Communications; -using OpenSim.Framework.Communications.Cache; -using OpenSim.Framework.Console; -using OpenSim.Framework.Servers; -using OpenSim.Region.Environment; -using OpenSim.Region.Environment.Scenes; -using OpenSim.Region.Physics.Manager; -using OpenSim.Region.Communications.VoiceChat; - -namespace OpenSim.Region.ClientStack -{ - public abstract class RegionApplicationBase - { - protected AssetCache m_assetCache; - protected Dictionary m_clientCircuits = new Dictionary(); - protected DateTime m_startuptime; - protected NetworkServersInfo m_networkServersInfo; - - protected BaseHttpServer m_httpServer; - protected uint m_httpServerPort; - - protected LogBase m_log; - protected CommunicationsManager m_commsManager; - - protected SceneManager m_sceneManager = new SceneManager(); - - protected StorageManager m_storageManager; - protected string m_storageConnectionString; - - protected VoiceChatServer m_voiceChatServer; - - public SceneManager SceneManager - { - get { return m_sceneManager; } - } - - public RegionApplicationBase() - { - m_startuptime = DateTime.Now; - } - - public virtual void StartUp() - { - ClientView.TerrainManager = new TerrainManager(new SecondLife()); - - m_storageManager = CreateStorageManager(m_storageConnectionString); - - Initialize(); - - m_httpServer = new BaseHttpServer(m_httpServerPort); - - m_log.Status("REGION", "Starting HTTP server"); - m_httpServer.Start(); - } - - protected abstract void Initialize(); - - protected void StartLog() - { - m_log = CreateLog(); - MainLog.Instance = m_log; - } - - protected abstract LogBase CreateLog(); - protected abstract PhysicsScene GetPhysicsScene(); - protected abstract StorageManager CreateStorageManager(string connectionstring); - - protected PhysicsScene GetPhysicsScene(string engine, string meshEngine) - { - PhysicsPluginManager physicsPluginManager; - physicsPluginManager = new PhysicsPluginManager(); - physicsPluginManager.LoadPlugins(); - return physicsPluginManager.GetPhysicsScene(engine, meshEngine); - } - - protected Scene SetupScene(RegionInfo regionInfo, out UDPServer udpServer, bool m_permissions) - { - AgentCircuitManager circuitManager = new AgentCircuitManager(); - udpServer = new UDPServer((uint) regionInfo.InternalEndPoint.Port, m_assetCache, m_log, circuitManager); - - Scene scene = CreateScene(regionInfo, m_storageManager, circuitManager); - m_voiceChatServer = new VoiceChatServer(scene); - - udpServer.LocalScene = scene; - - scene.LoadWorldMap(); - scene.RegisterRegionWithGrid(); - - scene.PhysicsScene = GetPhysicsScene(); - scene.PhysicsScene.SetTerrain(scene.Terrain.GetHeights1D()); - - //Master Avatar Setup - UserProfileData masterAvatar; - if (scene.RegionInfo.MasterAvatarAssignedUUID != LLUUID.Zero) - { - masterAvatar = m_commsManager.UserService.SetupMasterUser(scene.RegionInfo.MasterAvatarAssignedUUID); - } - else - { - masterAvatar = - m_commsManager.UserService.SetupMasterUser(scene.RegionInfo.MasterAvatarFirstName, - scene.RegionInfo.MasterAvatarLastName, - scene.RegionInfo.MasterAvatarSandboxPassword); - - } - - if (masterAvatar != null) - { - m_log.Verbose("PARCEL", "Found master avatar [" + masterAvatar.UUID.ToString() + "]"); - scene.RegionInfo.MasterAvatarAssignedUUID = masterAvatar.UUID; - } - else - { - m_log.Verbose("PARCEL", "No master avatar found, using null."); - scene.RegionInfo.MasterAvatarAssignedUUID = LLUUID.Zero; - } - - scene.LoadPrimsFromStorage(m_permissions); - scene.loadAllLandObjectsFromStorage(); - scene.performParcelPrimCountUpdate(); - scene.StartTimer(); - return scene; - } - - protected abstract Scene CreateScene(RegionInfo regionInfo, StorageManager storageManager, - AgentCircuitManager circuitManager); - } -} \ No newline at end of file +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ +using System; +using System.Collections.Generic; +using System.Net; +using libsecondlife; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Framework.Console; +using OpenSim.Framework.Servers; +using OpenSim.Region.Environment; +using OpenSim.Region.Environment.Scenes; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Communications.VoiceChat; + +namespace OpenSim.Region.ClientStack +{ + public abstract class RegionApplicationBase : BaseOpenSimServer + { + private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + protected AssetCache m_assetCache; + protected Dictionary m_clientCircuits = new Dictionary(); + protected NetworkServersInfo m_networkServersInfo; + + protected BaseHttpServer m_httpServer; + protected uint m_httpServerPort; + + protected CommunicationsManager m_commsManager; + + protected SceneManager m_sceneManager = new SceneManager(); + + protected StorageManager m_storageManager; + protected string m_storageConnectionString; + + // An attribute to indicate whether prim inventories should be persisted. + // Probably will be temporary until this stops being experimental. + protected bool m_storagePersistPrimInventories; + + protected VoiceChatServer m_voiceChatServer; + + public SceneManager SceneManager + { + get { return m_sceneManager; } + } + + public virtual void StartUp() + { + ClientView.TerrainManager = new TerrainManager(new SecondLife()); + + m_storageManager = CreateStorageManager(m_storageConnectionString); + + Initialize(); + + m_httpServer = new BaseHttpServer(m_httpServerPort); + + m_log.Info("[REGION]: Starting HTTP server"); + + m_httpServer.Start(); + } + + protected abstract void Initialize(); + + protected void StartConsole() + { + m_console = CreateConsole(); + MainConsole.Instance = m_console; + } + + protected abstract ConsoleBase CreateConsole(); + protected abstract PhysicsScene GetPhysicsScene(); + protected abstract StorageManager CreateStorageManager(string connectionstring); + + protected PhysicsScene GetPhysicsScene(string engine, string meshEngine) + { + PhysicsPluginManager physicsPluginManager; + physicsPluginManager = new PhysicsPluginManager(); + physicsPluginManager.LoadPlugins(); + return physicsPluginManager.GetPhysicsScene(engine, meshEngine); + } + + protected Scene SetupScene(RegionInfo regionInfo, out UDPServer udpServer, bool m_permissions) + { + AgentCircuitManager circuitManager = new AgentCircuitManager(); + IPAddress listenIP = regionInfo.InternalEndPoint.Address; + //if (!IPAddress.TryParse(regionInfo.InternalEndPoint, out listenIP)) + // listenIP = IPAddress.Parse("0.0.0.0"); + + uint port = (uint) regionInfo.InternalEndPoint.Port; + udpServer = new UDPServer(listenIP, ref port, regionInfo.m_allow_alternate_ports, m_assetCache, circuitManager); + regionInfo.InternalEndPoint.Port = (int)port; + + Scene scene = CreateScene(regionInfo, m_storageManager, circuitManager); + m_voiceChatServer = new VoiceChatServer(scene); + + udpServer.LocalScene = scene; + + scene.LoadWorldMap(); + scene.RegisterRegionWithGrid(); + + scene.PhysicsScene = GetPhysicsScene(); + scene.PhysicsScene.SetTerrain(scene.Terrain.GetHeights1D()); + + //Master Avatar Setup + UserProfileData masterAvatar; + if (scene.RegionInfo.MasterAvatarAssignedUUID != LLUUID.Zero) + { + masterAvatar = m_commsManager.UserService.SetupMasterUser(scene.RegionInfo.MasterAvatarAssignedUUID); + } + else + { + masterAvatar = + m_commsManager.UserService.SetupMasterUser(scene.RegionInfo.MasterAvatarFirstName, + scene.RegionInfo.MasterAvatarLastName, + scene.RegionInfo.MasterAvatarSandboxPassword); + + } + + if (masterAvatar != null) + { + m_log.Info("[PARCEL]: Found master avatar [" + masterAvatar.UUID.ToString() + "]"); + scene.RegionInfo.MasterAvatarAssignedUUID = masterAvatar.UUID; + } + else + { + m_log.Info("[PARCEL]: No master avatar found, using null."); + scene.RegionInfo.MasterAvatarAssignedUUID = LLUUID.Zero; + } + + scene.LoadPrimsFromStorage(m_permissions); + scene.loadAllLandObjectsFromStorage(); + scene.performParcelPrimCountUpdate(); + scene.StartTimer(); + return scene; + } + + protected abstract Scene CreateScene(RegionInfo regionInfo, StorageManager storageManager, + AgentCircuitManager circuitManager); + } +} diff --git a/OpenSim/Region/ClientStack/UDPServer.cs b/OpenSim/Region/ClientStack/UDPServer.cs index 6ea2bf6ad1..92fe827ba5 100644 --- a/OpenSim/Region/ClientStack/UDPServer.cs +++ b/OpenSim/Region/ClientStack/UDPServer.cs @@ -1,315 +1,419 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Net; -using System.Net.Sockets; -using libsecondlife.Packets; -using OpenSim.Framework; -using OpenSim.Framework.Communications.Cache; -using OpenSim.Framework.Console; - -namespace OpenSim.Region.ClientStack -{ - public class UDPServer : ClientStackNetworkHandler - { - protected Dictionary clientCircuits = new Dictionary(); - public Dictionary clientCircuits_reverse = new Dictionary(); - public Socket Server; - protected IPEndPoint ServerIncoming; - protected byte[] RecvBuffer = new byte[4096]; - protected byte[] ZeroBuffer = new byte[8192]; - protected IPEndPoint ipeSender; - protected EndPoint epSender; - protected AsyncCallback ReceivedData; - protected PacketServer m_packetServer; - protected ulong m_regionHandle; - - protected uint listenPort; - protected IScene m_localScene; - protected AssetCache m_assetCache; - protected LogBase m_log; - protected AgentCircuitManager m_authenticateSessionsClass; - - public PacketServer PacketServer - { - get { return m_packetServer; } - set { m_packetServer = value; } - } - - public IScene LocalScene - { - set - { - m_localScene = value; - m_packetServer.LocalScene = m_localScene; - m_regionHandle = m_localScene.RegionInfo.RegionHandle; - } - } - - public ulong RegionHandle - { - get { return m_regionHandle; } - } - - public UDPServer() - { - } - - public UDPServer(uint port, AssetCache assetCache, LogBase console, AgentCircuitManager authenticateClass) - { - listenPort = port; - m_assetCache = assetCache; - m_log = console; - m_authenticateSessionsClass = authenticateClass; - CreatePacketServer(); - } - - protected virtual void CreatePacketServer() - { - PacketServer packetServer = new PacketServer(this); - } - - protected virtual void OnReceivedData(IAsyncResult result) - { - ipeSender = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 0); - epSender = (EndPoint) ipeSender; - Packet packet = null; - - int numBytes; - - try - { - numBytes = Server.EndReceiveFrom(result, ref epSender); - - //Report byte count to rex statuswindow - OpenSim.Framework.ServerStatus.ServerStatus.ReportInPacketUdp(numBytes); - } - catch (SocketException e) - { - // TODO : Actually only handle those states that we have control over, re-throw everything else, - // TODO: implement cases as we encounter them. - switch (e.SocketErrorCode) - { - case SocketError.AlreadyInProgress: - case SocketError.NetworkReset: - case SocketError.ConnectionReset: - - try - { - CloseEndPoint(epSender); - } - catch (Exception a) - { - MainLog.Instance.Verbose("UDPSERVER", a.ToString()); - } - try - { - Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, - ReceivedData, null); - - // Ter: For some stupid reason ConnectionReset basically kills our async event structure.. - // so therefore.. we've got to tell the server to BeginReceiveFrom again. - // This will happen over and over until we've gone through all packets - // sent to and from this particular user. - // Stupid I know.. - // but Flusing the buffer would be even more stupid... so, we're stuck with this ugly method. - } - catch (SocketException) - { - } - break; - default: - try - { - CloseEndPoint(epSender); - } - catch (Exception) - { - //MainLog.Instance.Verbose("UDPSERVER", a.ToString()); - } - try - { - Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, - ReceivedData, null); - - // Ter: For some stupid reason ConnectionReset basically kills our async event structure.. - // so therefore.. we've got to tell the server to BeginReceiveFrom again. - // This will happen over and over until we've gone through all packets - // sent to and from this particular user. - // Stupid I know.. - // but Flusing the buffer would be even more stupid... so, we're stuck with this ugly method. - } - catch (SocketException) - { - } - - // Here's some reference code! :D - // Shutdown and restart the UDP listener! hehe - // Shiny - - //Server.Shutdown(SocketShutdown.Both); - //CloseEndPoint(epSender); - //ServerListener(); - break; - } - - return; - } - catch (ObjectDisposedException) - { - //MainLog.Instance.Debug("UDPSERVER", e.ToString()); - return; - } - - int packetEnd = numBytes - 1; - - try - { - packet = PacketPool.Instance.GetPacket(RecvBuffer, ref packetEnd, ZeroBuffer); - } - catch (Exception) - { - //MainLog.Instance.Debug("UDPSERVER", e.ToString()); - } - - if (packet != null) - { - // do we already have a circuit for this endpoint - uint circuit; - if (clientCircuits.TryGetValue(epSender, out circuit)) - { - //if so then send packet to the packetserver - //MainLog.Instance.Warn("UDPSERVER", "ALREADY HAVE Circuit!"); - m_packetServer.InPacket(circuit, packet); - } - else if (packet.Type == PacketType.UseCircuitCode) - { - // new client - MainLog.Instance.Debug("UDPSERVER", "Adding New Client"); - AddNewClient(packet); - } - else - { - // invalid client - //CFK: This message seems to have served its usefullness as of 12-15 so I am commenting it out for now - //m_log.Warn("client", "Got a packet from an invalid client - " + epSender.ToString()); - } - } - - Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, ReceivedData, null); - } - - private void CloseEndPoint(EndPoint sender) - { - uint circuit; - if (clientCircuits.TryGetValue(sender, out circuit)) - { - m_packetServer.CloseCircuit(circuit); - } - } - - protected virtual void AddNewClient(Packet packet) - { - UseCircuitCodePacket useCircuit = (UseCircuitCodePacket) packet; - clientCircuits.Add(epSender, useCircuit.CircuitCode.Code); - clientCircuits_reverse.Add(useCircuit.CircuitCode.Code, epSender); - - PacketServer.AddNewClient(epSender, useCircuit, m_assetCache, m_authenticateSessionsClass); - } - - public void ServerListener() - { - m_log.Verbose("SERVER", "Opening UDP socket on " + listenPort.ToString()); - - ServerIncoming = new IPEndPoint(IPAddress.Parse("0.0.0.0"), (int) listenPort); - Server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - Server.Bind(ServerIncoming); - - m_log.Verbose("SERVER", "UDP socket bound, getting ready to listen"); - - ipeSender = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 0); - epSender = (EndPoint) ipeSender; - ReceivedData = new AsyncCallback(OnReceivedData); - Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, ReceivedData, null); - - m_log.Status("SERVER", "Listening..."); - } - - public virtual void RegisterPacketServer(PacketServer server) - { - m_packetServer = server; - } - - public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode) - //EndPoint packetSender) - { - // find the endpoint for this circuit - EndPoint sendto = null; - if (clientCircuits_reverse.TryGetValue(circuitcode, out sendto)) - { - //we found the endpoint so send the packet to it - while (true) - { - try - { - Server.SendTo(buffer, size, flags, sendto); - return; - } - catch (SocketException e) - { - if (e.ErrorCode == 10055) - { - //Send buffer full, halt for half second and retry - MainLog.Instance.Warn("SERVER", "Socket send buffer was full, halting for 200ms"); - System.Threading.Thread.Sleep(200); - } - else - { - //Rethrow - throw e; - } - } - } - } - } - - public virtual void RemoveClientCircuit(uint circuitcode) - { - EndPoint sendto = null; - if (clientCircuits_reverse.TryGetValue(circuitcode, out sendto)) - { - clientCircuits.Remove(sendto); - - - clientCircuits_reverse.Remove(circuitcode); - } - } - } -} +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using libsecondlife.Packets; +using OpenSim.Framework; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Framework.Console; + +namespace OpenSim.Region.ClientStack +{ + public class UDPServer : ClientStackNetworkHandler + { + private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + protected Dictionary clientCircuits = new Dictionary(); + public Dictionary clientCircuits_reverse = new Dictionary(); + public Socket Server; + protected IPEndPoint ServerIncoming; + protected byte[] RecvBuffer = new byte[4096]; + protected byte[] ZeroBuffer = new byte[8192]; + protected IPEndPoint ipeSender; + protected EndPoint epSender; + protected AsyncCallback ReceivedData; + protected PacketServer m_packetServer; + protected ulong m_regionHandle; + + protected uint listenPort; + protected bool Allow_Alternate_Port; + protected IPAddress listenIP = IPAddress.Parse("0.0.0.0"); + protected IScene m_localScene; + protected AssetCache m_assetCache; + protected AgentCircuitManager m_authenticateSessionsClass; + + public PacketServer PacketServer + { + get { return m_packetServer; } + set { m_packetServer = value; } + } + + public IScene LocalScene + { + set + { + m_localScene = value; + m_packetServer.LocalScene = m_localScene; + m_regionHandle = m_localScene.RegionInfo.RegionHandle; + } + } + + public ulong RegionHandle + { + get { return m_regionHandle; } + } + + public UDPServer() + { + } + + public UDPServer(IPAddress _listenIP, ref uint port, bool allow_alternate_port, AssetCache assetCache, AgentCircuitManager authenticateClass) + { + listenIP = _listenIP; + listenPort = port; + Allow_Alternate_Port = allow_alternate_port; + m_assetCache = assetCache; + m_authenticateSessionsClass = authenticateClass; + CreatePacketServer(); + + // Return new port + // This because in Grid mode it is not really important what port the region listens to as long as it is correctly registered. + // So the option allow_alternate_ports="true" was added to default.xml + port = listenPort; + } + + protected virtual void CreatePacketServer() + { + PacketServer packetServer = new PacketServer(this); + } + + protected virtual void OnReceivedData(IAsyncResult result) + { + ipeSender = new IPEndPoint(listenIP, 0); + epSender = (EndPoint) ipeSender; + Packet packet = null; + + int numBytes = 1; + + try + { + numBytes = Server.EndReceiveFrom(result, ref epSender); + + //Report byte count to rex statuswindow + OpenSim.Framework.ServerStatus.ServerStatus.ReportInPacketUdp(numBytes); + } + catch (SocketException e) + { + // TODO : Actually only handle those states that we have control over, re-throw everything else, + // TODO: implement cases as we encounter them. + //m_log.Error("[UDPSERVER]: Connection Error! - " + e.ToString()); + switch (e.SocketErrorCode) + { + case SocketError.AlreadyInProgress: + case SocketError.NetworkReset: + case SocketError.ConnectionReset: + + try + { + CloseEndPoint(epSender); + } + catch (Exception a) + { + m_log.Info("[UDPSERVER]: " + a.ToString()); + } + try + { + Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, + ReceivedData, null); + + // Ter: For some stupid reason ConnectionReset basically kills our async event structure.. + // so therefore.. we've got to tell the server to BeginReceiveFrom again. + // This will happen over and over until we've gone through all packets + // sent to and from this particular user. + // Stupid I know.. + // but Flusing the buffer would be even more stupid... so, we're stuck with this ugly method. + } + catch (SocketException) + { + } + break; + default: + try + { + CloseEndPoint(epSender); + } + catch (Exception) + { + //m_log.Info("[UDPSERVER]" + a.ToString()); + } + try + { + Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, + ReceivedData, null); + + // Ter: For some stupid reason ConnectionReset basically kills our async event structure.. + // so therefore.. we've got to tell the server to BeginReceiveFrom again. + // This will happen over and over until we've gone through all packets + // sent to and from this particular user. + // Stupid I know.. + // but Flusing the buffer would be even more stupid... so, we're stuck with this ugly method. + } + catch (SocketException e2) + { + m_log.Error("[UDPSERVER]: " + e2.ToString()); + } + + // Here's some reference code! :D + // Shutdown and restart the UDP listener! hehe + // Shiny + + //Server.Shutdown(SocketShutdown.Both); + //CloseEndPoint(epSender); + //ServerListener(); + break; + } + + //return; + } + catch (ObjectDisposedException e) + { + m_log.Debug("[UDPSERVER]: " + e.ToString()); + try + { + Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, + ReceivedData, null); + + // Ter: For some stupid reason ConnectionReset basically kills our async event structure.. + // so therefore.. we've got to tell the server to BeginReceiveFrom again. + // This will happen over and over until we've gone through all packets + // sent to and from this particular user. + // Stupid I know.. + // but Flusing the buffer would be even more stupid... so, we're stuck with this ugly method. + } + catch (SocketException e2) + { + m_log.Error("[UDPSERVER]: " + e2.ToString()); + } + //return; + } + + int packetEnd = numBytes - 1; + + try + { + packet = PacketPool.Instance.GetPacket(RecvBuffer, ref packetEnd, ZeroBuffer); + + } + catch (Exception e) + { + m_log.Debug("[UDPSERVER]: " + e.ToString()); + } + + try + { + Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, ReceivedData, null); + } + catch (SocketException e4) + { + try + { + CloseEndPoint(epSender); + } + catch (Exception a) + { + m_log.Info("[UDPSERVER]: " + a.ToString()); + } + try + { + Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, + ReceivedData, null); + + // Ter: For some stupid reason ConnectionReset basically kills our async event structure.. + // so therefore.. we've got to tell the server to BeginReceiveFrom again. + // This will happen over and over until we've gone through all packets + // sent to and from this particular user. + // Stupid I know.. + // but Flusing the buffer would be even more stupid... so, we're stuck with this ugly method. + } + catch (SocketException e5) + { + m_log.Error("[UDPSERVER]: " + e5.ToString()); + } + + } + + if (packet != null) + { + try + { + // do we already have a circuit for this endpoint + uint circuit; + + bool ret = false; + lock (clientCircuits) + { + ret = clientCircuits.TryGetValue(epSender, out circuit); + } + if (ret) + { + //if so then send packet to the packetserver + //m_log.Warn("[UDPSERVER]: ALREADY HAVE Circuit!"); + m_packetServer.InPacket(circuit, packet); + } + else if (packet.Type == PacketType.UseCircuitCode) + { + // new client + m_log.Debug("[UDPSERVER]: Adding New Client"); + AddNewClient(packet); + } + else + { + // invalid client + //CFK: This message seems to have served its usefullness as of 12-15 so I am commenting it out for now + //m_log.Warn("[UDPSERVER]: Got a packet from an invalid client - " + packet.ToString()); + + } + } + catch (Exception ex) + { + m_log.Error("[UDPSERVER]: Exception in processing packet."); + m_log.Debug("[UDPSERVER]: Adding New Client"); + try + { + AddNewClient(packet); + } + catch (Exception e3) + { + m_log.Error("[UDPSERVER]: Adding New Client threw exception " + e3.ToString()); + Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, + ReceivedData, null); + } + } + } + + } + + private void CloseEndPoint(EndPoint sender) + { + uint circuit; + lock (clientCircuits) + { + if (clientCircuits.TryGetValue(sender, out circuit)) + { + m_packetServer.CloseCircuit(circuit); + } + } + } + + protected virtual void AddNewClient(Packet packet) + { + UseCircuitCodePacket useCircuit = (UseCircuitCodePacket) packet; + lock (clientCircuits) + { + if (!clientCircuits.ContainsKey(epSender)) + clientCircuits.Add(epSender, useCircuit.CircuitCode.Code); + else + m_log.Error("[UDPSERVER]: clientCircuits already contans entry for user " + useCircuit.CircuitCode.Code.ToString() + ". NOT adding."); + } + lock (clientCircuits_reverse) + { + if (!clientCircuits_reverse.ContainsKey(useCircuit.CircuitCode.Code)) + clientCircuits_reverse.Add(useCircuit.CircuitCode.Code, epSender); + else + m_log.Error("[UDPSERVER]: clientCurcuits_reverse already contains entry for user " + useCircuit.CircuitCode.Code.ToString() + ". NOT adding."); + } + + PacketServer.AddNewClient(epSender, useCircuit, m_assetCache, m_authenticateSessionsClass); + } + + public void ServerListener() + { + uint newPort = listenPort; + for (uint i = 0; i < 20; i++) + { + newPort = listenPort + i; + m_log.Info("[SERVER]: Opening UDP socket on " + listenIP.ToString() + " " + newPort + ".");// Allow alternate ports: " + Allow_Alternate_Port.ToString()); + try + { + ServerIncoming = new IPEndPoint(listenIP, (int) newPort); + Server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + Server.Bind(ServerIncoming); + listenPort = newPort; + break; + } + catch (Exception ex) + { + // We are not looking for alternate ports? + //if (!Allow_Alternate_Port) + throw (ex); + + // We are looking for alternate ports! + m_log.Info("[SERVER]: UDP socket on " + listenIP.ToString() + " " + listenPort.ToString() + " is not available, trying next."); + } + System.Threading.Thread.Sleep(100); // Wait before we retry socket + } + + m_log.Info("[SERVER]: UDP socket bound, getting ready to listen"); + + ipeSender = new IPEndPoint(listenIP, 0); + epSender = (EndPoint) ipeSender; + ReceivedData = new AsyncCallback(OnReceivedData); + Server.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, ReceivedData, null); + + m_log.Info("[SERVER]: Listening on port " + newPort); + } + + public virtual void RegisterPacketServer(PacketServer server) + { + m_packetServer = server; + } + + public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode) + //EndPoint packetSender) + { + // find the endpoint for this circuit + EndPoint sendto = null; + lock (clientCircuits_reverse) + { + if (clientCircuits_reverse.TryGetValue(circuitcode, out sendto)) + { + //we found the endpoint so send the packet to it + Server.SendTo(buffer, size, flags, sendto); + } + } + } + + public virtual void RemoveClientCircuit(uint circuitcode) + { + EndPoint sendto = null; + lock (clientCircuits_reverse) + { + if (clientCircuits_reverse.TryGetValue(circuitcode, out sendto)) + { + clientCircuits.Remove(sendto); + + + clientCircuits_reverse.Remove(circuitcode); + } + } + } + } +}