mirror of
https://github.com/opensim/opensim.git
synced 2026-08-06 01:06:30 +08:00
* Commit 2/3 - Please dont attempt to update to this revision until all 3 are in.
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.Sockets;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
public interface LLClientStackNetworkHandler
|
||||
{
|
||||
void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode); // EndPoint packetSender);
|
||||
void RemoveClientCircuit(uint circuitcode);
|
||||
void RegisterPacketServer(LLPacketServer server);
|
||||
}
|
||||
}
|
||||
4927
OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
Normal file
4927
OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
Normal file
File diff suppressed because it is too large
Load Diff
532
OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
Normal file
532
OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* 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.LindenUDP
|
||||
{
|
||||
public class LLPacketQueue : IPullStatsProvider
|
||||
{
|
||||
//private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private bool m_enabled = true;
|
||||
|
||||
private BlockingQueue<LLQueItem> SendQueue;
|
||||
|
||||
private Queue<LLQueItem> IncomingPacketQueue;
|
||||
private Queue<LLQueItem> OutgoingPacketQueue;
|
||||
private Queue<LLQueItem> ResendOutgoingPacketQueue;
|
||||
private Queue<LLQueItem> LandOutgoingPacketQueue;
|
||||
private Queue<LLQueItem> WindOutgoingPacketQueue;
|
||||
private Queue<LLQueItem> CloudOutgoingPacketQueue;
|
||||
private Queue<LLQueItem> TaskOutgoingPacketQueue;
|
||||
private Queue<LLQueItem> TextureOutgoingPacketQueue;
|
||||
private Queue<LLQueItem> AssetOutgoingPacketQueue;
|
||||
|
||||
private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>();
|
||||
private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>();
|
||||
|
||||
// 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 LLPacketThrottle ResendThrottle;
|
||||
private LLPacketThrottle LandThrottle;
|
||||
private LLPacketThrottle WindThrottle;
|
||||
private LLPacketThrottle CloudThrottle;
|
||||
private LLPacketThrottle TaskThrottle;
|
||||
private LLPacketThrottle AssetThrottle;
|
||||
private LLPacketThrottle TextureThrottle;
|
||||
private LLPacketThrottle TotalThrottle;
|
||||
|
||||
// private long LastThrottle;
|
||||
// private long ThrottleInterval;
|
||||
private Timer throttleTimer;
|
||||
|
||||
private LLUUID m_agentId;
|
||||
|
||||
public LLPacketQueue(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<LLQueItem>();
|
||||
|
||||
IncomingPacketQueue = new Queue<LLQueItem>();
|
||||
OutgoingPacketQueue = new Queue<LLQueItem>();
|
||||
ResendOutgoingPacketQueue = new Queue<LLQueItem>();
|
||||
LandOutgoingPacketQueue = new Queue<LLQueItem>();
|
||||
WindOutgoingPacketQueue = new Queue<LLQueItem>();
|
||||
CloudOutgoingPacketQueue = new Queue<LLQueItem>();
|
||||
TaskOutgoingPacketQueue = new Queue<LLQueItem>();
|
||||
TextureOutgoingPacketQueue = new Queue<LLQueItem>();
|
||||
AssetOutgoingPacketQueue = new Queue<LLQueItem>();
|
||||
|
||||
|
||||
// Set up the throttle classes (min, max, current) in bytes
|
||||
ResendThrottle = new LLPacketThrottle(5000, 100000, 16000);
|
||||
LandThrottle = new LLPacketThrottle(1000, 100000, 2000);
|
||||
WindThrottle = new LLPacketThrottle(1000, 100000, 1000);
|
||||
CloudThrottle = new LLPacketThrottle(1000, 100000, 1000);
|
||||
TaskThrottle = new LLPacketThrottle(1000, 800000, 3000);
|
||||
AssetThrottle = new LLPacketThrottle(1000, 800000, 1000);
|
||||
TextureThrottle = new LLPacketThrottle(1000, 800000, 4000);
|
||||
// Total Throttle trumps all
|
||||
// Number of bytes allowed to go out per second. (256kbps per client)
|
||||
TotalThrottle = new LLPacketThrottle(0, 1500000, 28000);
|
||||
|
||||
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(LLQueItem item)
|
||||
{
|
||||
if (!m_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// We could micro lock, but that will tend to actually
|
||||
// probably be worse than just synchronizing on SendQueue
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
SendQueue.Enqueue(item);
|
||||
return;
|
||||
}
|
||||
|
||||
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 LLQueItem 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()
|
||||
{
|
||||
Flush();
|
||||
|
||||
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)
|
||||
{
|
||||
LLQueItem qpack = ResendOutgoingPacketQueue.Dequeue();
|
||||
|
||||
SendQueue.Enqueue(qpack);
|
||||
TotalThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
ResendThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
}
|
||||
if (LandThrottle.UnderLimit() && LandOutgoingPacketQueue.Count > 0)
|
||||
{
|
||||
LLQueItem qpack = LandOutgoingPacketQueue.Dequeue();
|
||||
|
||||
SendQueue.Enqueue(qpack);
|
||||
TotalThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
LandThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
}
|
||||
if (WindThrottle.UnderLimit() && WindOutgoingPacketQueue.Count > 0)
|
||||
{
|
||||
LLQueItem qpack = WindOutgoingPacketQueue.Dequeue();
|
||||
|
||||
SendQueue.Enqueue(qpack);
|
||||
TotalThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
WindThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
}
|
||||
if (CloudThrottle.UnderLimit() && CloudOutgoingPacketQueue.Count > 0)
|
||||
{
|
||||
LLQueItem qpack = CloudOutgoingPacketQueue.Dequeue();
|
||||
|
||||
SendQueue.Enqueue(qpack);
|
||||
TotalThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
CloudThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
}
|
||||
if (TaskThrottle.UnderLimit() && TaskOutgoingPacketQueue.Count > 0)
|
||||
{
|
||||
LLQueItem qpack = TaskOutgoingPacketQueue.Dequeue();
|
||||
|
||||
SendQueue.Enqueue(qpack);
|
||||
TotalThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
TaskThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
}
|
||||
if (TextureThrottle.UnderLimit() && TextureOutgoingPacketQueue.Count > 0)
|
||||
{
|
||||
LLQueItem qpack = TextureOutgoingPacketQueue.Dequeue();
|
||||
|
||||
SendQueue.Enqueue(qpack);
|
||||
TotalThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
TextureThrottle.Add(qpack.Packet.ToBytes().Length);
|
||||
}
|
||||
if (AssetThrottle.UnderLimit() && AssetOutgoingPacketQueue.Count > 0)
|
||||
{
|
||||
LLQueItem 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 LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem 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 static int ScaleThrottle(int value, int curmax, int newmax)
|
||||
{
|
||||
return (value / 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);
|
||||
}
|
||||
|
||||
public LLQueItem[] GetQueueArray()
|
||||
{
|
||||
return SendQueue.GetQueueArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
150
OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs
Normal file
150
OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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;
|
||||
using OpenSim.Region.ClientStack.LindenUDP;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
public class LLPacketServer
|
||||
{
|
||||
//private static readonly log4net.ILog m_log
|
||||
// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private LLClientStackNetworkHandler m_networkHandler;
|
||||
private IScene m_scene;
|
||||
|
||||
//private readonly ClientManager m_clientManager = new ClientManager();
|
||||
//public ClientManager ClientManager
|
||||
//{
|
||||
// get { return m_clientManager; }
|
||||
//}
|
||||
|
||||
public LLPacketServer(LLClientStackNetworkHandler networkHandler)
|
||||
{
|
||||
m_networkHandler = networkHandler;
|
||||
m_networkHandler.RegisterPacketServer(this);
|
||||
}
|
||||
|
||||
public IScene LocalScene
|
||||
{
|
||||
set { m_scene = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="circuitCode"></param>
|
||||
/// <param name="packet"></param>
|
||||
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,
|
||||
LLPacketServer packServer, AgentCircuitManager authenSessions,
|
||||
LLUUID agentId, LLUUID sessionId, uint circuitCode, EndPoint proxyEP)
|
||||
{
|
||||
return
|
||||
new LLClientView(remoteEP, scene, assetCache, packServer, authenSessions, agentId, sessionId, circuitCode, proxyEP);
|
||||
}
|
||||
|
||||
public virtual bool AddNewClient(EndPoint epSender, UseCircuitCodePacket useCircuit, AssetCache assetCache,
|
||||
AgentCircuitManager authenticateSessionsClass, EndPoint proxyEP)
|
||||
{
|
||||
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, proxyEP);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="flags"></param>
|
||||
/// <param name="circuitcode"></param>
|
||||
public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode)
|
||||
{
|
||||
m_networkHandler.SendPacketTo(buffer, size, flags, circuitcode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="circuitcode"></param>
|
||||
public virtual void CloseCircuit(uint circuitcode)
|
||||
{
|
||||
m_networkHandler.RemoveClientCircuit(circuitcode);
|
||||
|
||||
//m_scene.ClientManager.CloseAllAgents(circuitcode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completely close down the given client.
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
public virtual void CloseClient(IClientAPI client)
|
||||
{
|
||||
//m_log.Info("PacketServer:CloseClient()");
|
||||
|
||||
CloseCircuit(client.CircuitCode);
|
||||
m_scene.ClientManager.Remove(client.CircuitCode);
|
||||
client.Close(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
93
OpenSim/Region/ClientStack/LindenUDP/LLPacketThrottle.cs
Normal file
93
OpenSim/Region/ClientStack/LindenUDP/LLPacketThrottle.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
public class LLPacketThrottle
|
||||
{
|
||||
private int max; // max allowable throttle
|
||||
private int min; // min allowable throttle
|
||||
private int throttle; // current throttle setting
|
||||
private static int divisor = 7; // the throttle time divisor, this probably should factor out
|
||||
private int sent; // current number of bytes sent
|
||||
|
||||
public LLPacketThrottle(int Min, int Max, int Throttle)
|
||||
{
|
||||
max = Max;
|
||||
min = Min;
|
||||
throttle = Throttle;
|
||||
sent = 0;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
sent = 0;
|
||||
}
|
||||
|
||||
public bool UnderLimit()
|
||||
{
|
||||
return (sent < (throttle/divisor));
|
||||
}
|
||||
|
||||
public int Add(int bytes)
|
||||
{
|
||||
sent += bytes;
|
||||
return sent;
|
||||
}
|
||||
|
||||
// Properties
|
||||
public int Max
|
||||
{
|
||||
get { return max; }
|
||||
}
|
||||
|
||||
public int Min
|
||||
{
|
||||
get { return min; }
|
||||
}
|
||||
|
||||
public int Throttle
|
||||
{
|
||||
get { return throttle; }
|
||||
set
|
||||
{
|
||||
if (value > max)
|
||||
{
|
||||
throttle = max;
|
||||
}
|
||||
else if (value < min)
|
||||
{
|
||||
throttle = min;
|
||||
}
|
||||
else
|
||||
{
|
||||
throttle = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs
Normal file
43
OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 libsecondlife.Packets;
|
||||
using OpenSim.Framework;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
public class LLQueItem
|
||||
{
|
||||
public LLQueItem()
|
||||
{
|
||||
}
|
||||
|
||||
public Packet Packet;
|
||||
public bool Incoming;
|
||||
public ThrottleOutPacketType throttleType;
|
||||
}
|
||||
}
|
||||
499
OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
Normal file
499
OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
Normal file
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
* 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 System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using libsecondlife.Packets;
|
||||
using log4net;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Communications.Cache;
|
||||
using OpenSim.Region.ClientStack.LindenUDP;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
public class LLUDPServer : LLClientStackNetworkHandler, IClientNetworkServer
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
protected Dictionary<EndPoint, uint> clientCircuits = new Dictionary<EndPoint, uint>();
|
||||
public Dictionary<uint, EndPoint> clientCircuits_reverse = new Dictionary<uint, EndPoint>();
|
||||
protected Dictionary<uint, EndPoint> proxyCircuits = new Dictionary<uint, EndPoint>();
|
||||
private Socket m_socket;
|
||||
protected IPEndPoint ServerIncoming;
|
||||
protected byte[] RecvBuffer = new byte[4096];
|
||||
protected byte[] ZeroBuffer = new byte[8192];
|
||||
protected IPEndPoint ipeSender;
|
||||
protected EndPoint epSender;
|
||||
protected EndPoint epProxy;
|
||||
protected int proxyPortOffset;
|
||||
protected AsyncCallback ReceivedData;
|
||||
protected LLPacketServer m_packetServer;
|
||||
protected Location m_location;
|
||||
|
||||
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 LLPacketServer PacketServer
|
||||
{
|
||||
get { return m_packetServer; }
|
||||
set { m_packetServer = value; }
|
||||
}
|
||||
|
||||
public IScene LocalScene
|
||||
{
|
||||
set
|
||||
{
|
||||
m_localScene = value;
|
||||
m_packetServer.LocalScene = m_localScene;
|
||||
m_location = new Location(m_localScene.RegionInfo.RegionHandle);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong RegionHandle
|
||||
{
|
||||
get { return m_location.RegionHandle; }
|
||||
}
|
||||
|
||||
Socket IClientNetworkServer.Server
|
||||
{
|
||||
get { return m_socket; }
|
||||
}
|
||||
|
||||
public bool HandlesRegion(Location x)
|
||||
{
|
||||
return x == m_location;
|
||||
}
|
||||
|
||||
public LLUDPServer()
|
||||
{
|
||||
}
|
||||
|
||||
public LLUDPServer(IPAddress _listenIP, ref uint port, int proxyPortOffset, bool allow_alternate_port, AssetCache assetCache, AgentCircuitManager authenticateClass)
|
||||
{
|
||||
this.proxyPortOffset = proxyPortOffset;
|
||||
listenPort = (uint) (port + proxyPortOffset);
|
||||
listenIP = _listenIP;
|
||||
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 = (uint)(listenPort - proxyPortOffset);
|
||||
}
|
||||
|
||||
protected virtual void CreatePacketServer()
|
||||
{
|
||||
LLPacketServer packetServer = new LLPacketServer(this);
|
||||
}
|
||||
|
||||
protected virtual void OnReceivedData(IAsyncResult result)
|
||||
{
|
||||
ipeSender = new IPEndPoint(listenIP, 0);
|
||||
epSender = (EndPoint) ipeSender;
|
||||
Packet packet = null;
|
||||
|
||||
int numBytes = 1;
|
||||
|
||||
try
|
||||
{
|
||||
numBytes = m_socket.EndReceiveFrom(result, ref epSender);
|
||||
}
|
||||
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
|
||||
{
|
||||
m_socket.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
|
||||
{
|
||||
m_socket.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
|
||||
{
|
||||
m_socket.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());
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
//return;
|
||||
}
|
||||
|
||||
//System.Console.WriteLine("UDPServer : recieved message from {0}", epSender.ToString());
|
||||
epProxy = epSender;
|
||||
if (proxyPortOffset != 0)
|
||||
{
|
||||
epSender = PacketPool.DecodeProxyMessage(RecvBuffer, ref numBytes);
|
||||
}
|
||||
|
||||
int packetEnd = numBytes - 1;
|
||||
|
||||
try
|
||||
{
|
||||
packet = PacketPool.Instance.GetPacket(RecvBuffer, ref packetEnd, ZeroBuffer);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Debug("[UDPSERVER]: " + e.ToString());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_socket.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, ReceivedData, null);
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
try
|
||||
{
|
||||
CloseEndPoint(epSender);
|
||||
}
|
||||
catch (Exception a)
|
||||
{
|
||||
m_log.Info("[UDPSERVER]: " + a.ToString());
|
||||
}
|
||||
try
|
||||
{
|
||||
m_socket.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());
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
UseCircuitCodePacket p = (UseCircuitCodePacket)packet;
|
||||
|
||||
// Ack the first UseCircuitCode packet
|
||||
PacketAckPacket ack_it = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
|
||||
// TODO: don't create new blocks if recycling an old packet
|
||||
ack_it.Packets = new PacketAckPacket.PacketsBlock[1];
|
||||
ack_it.Packets[0] = new PacketAckPacket.PacketsBlock();
|
||||
ack_it.Packets[0].ID = packet.Header.Sequence;
|
||||
ack_it.Header.Reliable = false;
|
||||
SendPacketTo(ack_it.ToBytes(),ack_it.ToBytes().Length,SocketFlags.None,p.CircuitCode.Code);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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());
|
||||
m_socket.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)
|
||||
{
|
||||
//Slave regions don't accept new clients
|
||||
if(m_localScene.Region_Status != RegionStatus.SlaveScene)
|
||||
{
|
||||
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.");
|
||||
}
|
||||
|
||||
lock (proxyCircuits)
|
||||
{
|
||||
if (!proxyCircuits.ContainsKey(useCircuit.CircuitCode.Code))
|
||||
proxyCircuits.Add(useCircuit.CircuitCode.Code, epProxy);
|
||||
else
|
||||
m_log.Error("[UDPSERVER]: proxyCircuits already contains entry for user " + useCircuit.CircuitCode.Code.ToString() + ". NOT adding.");
|
||||
}
|
||||
|
||||
PacketServer.AddNewClient(epSender, useCircuit, m_assetCache, m_authenticateSessionsClass, epProxy);
|
||||
}
|
||||
PacketPool.Instance.ReturnPacket(packet);
|
||||
}
|
||||
|
||||
public void ServerListener()
|
||||
{
|
||||
uint newPort = listenPort;
|
||||
m_log.Info("[SERVER]: Opening UDP socket on " + listenIP.ToString() + " " + newPort + ".");
|
||||
|
||||
ServerIncoming = new IPEndPoint(listenIP, (int)newPort);
|
||||
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
m_socket.Bind(ServerIncoming);
|
||||
listenPort = newPort;
|
||||
|
||||
m_log.Info("[SERVER]: UDP socket bound, getting ready to listen");
|
||||
|
||||
ipeSender = new IPEndPoint(listenIP, 0);
|
||||
epSender = (EndPoint)ipeSender;
|
||||
ReceivedData = new AsyncCallback(OnReceivedData);
|
||||
m_socket.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref epSender, ReceivedData, null);
|
||||
|
||||
m_log.Info("[SERVER]: Listening on port " + newPort);
|
||||
}
|
||||
|
||||
public virtual void RegisterPacketServer(LLPacketServer 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
|
||||
if (proxyPortOffset != 0)
|
||||
{
|
||||
//MainLog.Instance.Verbose("UDPSERVER", "SendPacketTo proxy " + proxyCircuits[circuitcode].ToString() + ": client " + sendto.ToString());
|
||||
PacketPool.EncodeProxyMessage(buffer, ref size, sendto);
|
||||
m_socket.SendTo(buffer, size, flags, proxyCircuits[circuitcode]);
|
||||
}
|
||||
else
|
||||
{
|
||||
//MainLog.Instance.Verbose("UDPSERVER", "SendPacketTo : client " + sendto.ToString());
|
||||
m_socket.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);
|
||||
proxyCircuits.Remove(circuitcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RestoreClient(AgentCircuitData circuit, EndPoint userEP, EndPoint proxyEP)
|
||||
{
|
||||
//MainLog.Instance.Verbose("UDPSERVER", "RestoreClient");
|
||||
|
||||
UseCircuitCodePacket useCircuit = new UseCircuitCodePacket();
|
||||
useCircuit.CircuitCode.Code = circuit.circuitcode;
|
||||
useCircuit.CircuitCode.ID = circuit.AgentID;
|
||||
useCircuit.CircuitCode.SessionID = circuit.SessionID;
|
||||
|
||||
lock (clientCircuits)
|
||||
{
|
||||
if (!clientCircuits.ContainsKey(userEP))
|
||||
clientCircuits.Add(userEP, 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, userEP);
|
||||
else
|
||||
m_log.Error("[UDPSERVER]: clientCurcuits_reverse already contains entry for user " + useCircuit.CircuitCode.Code.ToString() + ". NOT adding.");
|
||||
}
|
||||
|
||||
lock (proxyCircuits)
|
||||
{
|
||||
if (!proxyCircuits.ContainsKey(useCircuit.CircuitCode.Code))
|
||||
{
|
||||
proxyCircuits.Add(useCircuit.CircuitCode.Code, proxyEP);
|
||||
}
|
||||
else
|
||||
{
|
||||
// re-set proxy endpoint
|
||||
proxyCircuits.Remove(useCircuit.CircuitCode.Code);
|
||||
proxyCircuits.Add(useCircuit.CircuitCode.Code, proxyEP);
|
||||
}
|
||||
}
|
||||
|
||||
PacketServer.AddNewClient(userEP, useCircuit, m_assetCache, m_authenticateSessionsClass, proxyEP);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user