* Commit 2/3 - Please dont attempt to update to this revision until all 3 are in.

This commit is contained in:
Adam Frisby
2008-05-02 16:41:08 +00:00
parent c6236b5cf3
commit 29b8c84cea
14 changed files with 6339 additions and 6367 deletions

View File

@@ -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);
}
}

File diff suppressed because it is too large Load Diff

View 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();
}
}
}

View 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);
}
}
}

View 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;
}
}
}
}
}

View 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;
}
}

View 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);
}
}
}