Merge branch 'master' into vehicles

0.6.8-post-fixes
Melanie 2009-10-15 21:14:13 +01:00
commit 6deef7d0f3
63 changed files with 2254 additions and 1996 deletions

View File

@ -774,6 +774,11 @@ namespace OpenSim.Client.MXP.ClientStack
get { return m_sessionID.CRC(); }
}
public IPEndPoint RemoteEndPoint
{
get { return Session.RemoteEndPoint; }
}
public void SetDebugPacketLevel(int newDebug)
{
//m_debugLevel = newDebug;
@ -798,9 +803,9 @@ namespace OpenSim.Client.MXP.ClientStack
OnConnectionClosed(this);
}
public void Close(bool ShutdownCircuit)
public void Close()
{
m_log.Info("[MXP ClientStack] Close Called with SC=" + ShutdownCircuit);
m_log.Info("[MXP ClientStack] Close Called");
// Tell the client to go
SendLogoutPacket();
@ -815,7 +820,7 @@ namespace OpenSim.Client.MXP.ClientStack
public void Kick(string message)
{
Close(false);
Close();
}
public void Start()
@ -1448,7 +1453,7 @@ namespace OpenSim.Client.MXP.ClientStack
public void Terminate()
{
Close(false);
Close();
}
public void SendSetFollowCamProperties(UUID objectID, SortedDictionary<int, float> parameters)
@ -1615,12 +1620,12 @@ namespace OpenSim.Client.MXP.ClientStack
public void Disconnect(string reason)
{
Kick(reason);
Close(true);
Close();
}
public void Disconnect()
{
Close(true);
Close();
}
#endregion

View File

@ -339,12 +339,6 @@ namespace OpenSim.Client.MXP.PacketHandler
m_clients.Add(client);
m_log.Debug("[MXP ClientStack]: Created ClientView.");
m_log.Debug("[MXP ClientStack]: Adding ClientView to Scene...");
scene.ClientManager.Add(client.CircuitCode, client);
m_log.Debug("[MXP ClientStack]: Added ClientView to Scene.");
client.MXPSendSynchronizationBegin(m_scenes[new UUID(joinRequestMessage.BubbleId)].SceneContents.GetTotalObjectsCount());
m_log.Debug("[MXP ClientStack]: Starting ClientView...");

View File

@ -207,6 +207,11 @@ namespace OpenSim.Client.VWoHTTP.ClientStack
get { throw new System.NotImplementedException(); }
}
public IPEndPoint RemoteEndPoint
{
get { throw new System.NotImplementedException(); }
}
public event GenericMessage OnGenericMessage = delegate { };
public event ImprovedInstantMessage OnInstantMessage = delegate { };
public event ChatMessage OnChatFromClient = delegate { };
@ -415,7 +420,7 @@ namespace OpenSim.Client.VWoHTTP.ClientStack
throw new System.NotImplementedException();
}
public void Close(bool ShutdownCircuit)
public void Close()
{
throw new System.NotImplementedException();
}

View File

@ -151,7 +151,10 @@ namespace OpenSim.Data.MySQL
DataTable schemaTable = result.GetSchemaTable();
foreach (DataRow row in schemaTable.Rows)
m_ColumnNames.Add(row["ColumnName"].ToString());
{
if (row["ColumnName"] != null)
m_ColumnNames.Add(row["ColumnName"].ToString());
}
}
foreach (string s in m_ColumnNames)

View File

@ -28,193 +28,195 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using log4net;
using System.Net;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace OpenSim.Framework
{
public delegate void ForEachClientDelegate(IClientAPI client);
/// <summary>
/// Maps from client AgentID and RemoteEndPoint values to IClientAPI
/// references for all of the connected clients
/// </summary>
public class ClientManager
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>A dictionary mapping from <seealso cref="UUID"/>
/// to <seealso cref="IClientAPI"/> references</summary>
private Dictionary<UUID, IClientAPI> m_dict1;
/// <summary>A dictionary mapping from <seealso cref="IPEndPoint"/>
/// to <seealso cref="IClientAPI"/> references</summary>
private Dictionary<IPEndPoint, IClientAPI> m_dict2;
/// <summary>An immutable collection of <seealso cref="IClientAPI"/>
/// references</summary>
private IClientAPI[] m_array;
/// <summary>Synchronization object for writing to the collections</summary>
private object m_syncRoot = new object();
private Dictionary<uint, IClientAPI> m_clients;
/// <summary>Number of clients in the collection</summary>
public int Count { get { return m_dict1.Count; } }
/// <summary>
/// Default constructor
/// </summary>
public ClientManager()
{
m_clients = new Dictionary<uint, IClientAPI>();
m_dict1 = new Dictionary<UUID, IClientAPI>();
m_dict2 = new Dictionary<IPEndPoint, IClientAPI>();
m_array = new IClientAPI[0];
}
public void ForEachClient(ForEachClientDelegate whatToDo)
/// <summary>
/// Add a client reference to the collection if it does not already
/// exist
/// </summary>
/// <param name="value">Reference to the client object</param>
/// <returns>True if the client reference was successfully added,
/// otherwise false if the given key already existed in the collection</returns>
public bool Add(IClientAPI value)
{
IClientAPI[] LocalClients;
lock (m_clients)
lock (m_syncRoot)
{
LocalClients = new IClientAPI[m_clients.Count];
m_clients.Values.CopyTo(LocalClients, 0);
if (m_dict1.ContainsKey(value.AgentId) || m_dict2.ContainsKey(value.RemoteEndPoint))
return false;
m_dict1[value.AgentId] = value;
m_dict2[value.RemoteEndPoint] = value;
IClientAPI[] oldArray = m_array;
int oldLength = oldArray.Length;
IClientAPI[] newArray = new IClientAPI[oldLength + 1];
for (int i = 0; i < oldLength; i++)
newArray[i] = oldArray[i];
newArray[oldLength] = value;
m_array = newArray;
}
for (int i = 0; i < LocalClients.Length; i++)
return true;
}
/// <summary>
/// Remove a client from the collection
/// </summary>
/// <param name="key">UUID of the client to remove</param>
/// <returns>True if a client was removed, or false if the given UUID
/// was not present in the collection</returns>
public bool Remove(UUID key)
{
lock (m_syncRoot)
{
try
IClientAPI value;
if (m_dict1.TryGetValue(key, out value))
{
whatToDo(LocalClients[i]);
}
catch (Exception e)
{
m_log.Warn("[CLIENT]: Unable to do ForEachClient for one of the clients" + "\n Reason: " + e.ToString());
m_dict1.Remove(key);
m_dict2.Remove(value.RemoteEndPoint);
IClientAPI[] oldArray = m_array;
int oldLength = oldArray.Length;
IClientAPI[] newArray = new IClientAPI[oldLength - 1];
int j = 0;
for (int i = 0; i < oldLength; i++)
{
if (oldArray[i] != value)
newArray[j++] = oldArray[i];
}
m_array = newArray;
return true;
}
}
return false;
}
public void Remove(uint id)
/// <summary>
/// Resets the client collection
/// </summary>
public void Clear()
{
lock (m_clients)
lock (m_syncRoot)
{
m_clients.Remove(id);
}
}
public void Add(uint id, IClientAPI client)
{
lock (m_clients)
{
m_clients.Add(id, client);
m_dict1.Clear();
m_dict2.Clear();
m_array = new IClientAPI[0];
}
}
/// <summary>
/// Pass incoming packet to client.
/// Checks if a UUID is in the collection
/// </summary>
/// <param name="circuitCode">uint identifying the connection/client.</param>
/// <param name="packet">object containing the packet.</param>
public void InPacket(uint circuitCode, object packet)
/// <param name="key">UUID to check for</param>
/// <returns>True if the UUID was found in the collection, otherwise false</returns>
public bool ContainsKey(UUID key)
{
IClientAPI client;
bool tryGetRet = false;
lock (m_clients)
tryGetRet = m_clients.TryGetValue(circuitCode, out client);
if (tryGetRet)
return m_dict1.ContainsKey(key);
}
/// <summary>
/// Checks if an endpoint is in the collection
/// </summary>
/// <param name="key">Endpoint to check for</param>
/// <returns>True if the endpoint was found in the collection, otherwise false</returns>
public bool ContainsKey(IPEndPoint key)
{
return m_dict2.ContainsKey(key);
}
/// <summary>
/// Attempts to fetch a value out of the collection
/// </summary>
/// <param name="key">UUID of the client to retrieve</param>
/// <param name="value">Retrieved client, or null on lookup failure</param>
/// <returns>True if the lookup succeeded, otherwise false</returns>
public bool TryGetValue(UUID key, out IClientAPI value)
{
try { return m_dict1.TryGetValue(key, out value); }
catch (Exception)
{
client.InPacket(packet);
value = null;
return false;
}
}
public void CloseAllAgents(uint circuitCode)
/// <summary>
/// Attempts to fetch a value out of the collection
/// </summary>
/// <param name="key">Endpoint of the client to retrieve</param>
/// <param name="value">Retrieved client, or null on lookup failure</param>
/// <returns>True if the lookup succeeded, otherwise false</returns>
public bool TryGetValue(IPEndPoint key, out IClientAPI value)
{
IClientAPI client;
bool tryGetRet = false;
lock (m_clients)
tryGetRet = m_clients.TryGetValue(circuitCode, out client);
if (tryGetRet)
try { return m_dict2.TryGetValue(key, out value); }
catch (Exception)
{
CloseAllCircuits(client.AgentId);
value = null;
return false;
}
}
public void CloseAllCircuits(UUID agentId)
/// <summary>
/// Performs a given task in parallel for each of the elements in the
/// collection
/// </summary>
/// <param name="action">Action to perform on each element</param>
public void ForEach(Action<IClientAPI> action)
{
uint[] circuits = GetAllCircuits(agentId);
// We're using a for loop here so changes to the circuits don't cause it to completely fail.
for (int i = 0; i < circuits.Length; i++)
{
IClientAPI client;
try
{
bool tryGetRet = false;
lock (m_clients)
tryGetRet = m_clients.TryGetValue(circuits[i], out client);
if (tryGetRet)
{
Remove(client.CircuitCode);
client.Close(false);
}
}
catch (Exception e)
{
m_log.Error(string.Format("[CLIENT]: Unable to shutdown circuit for: {0}\n Reason: {1}", agentId, e));
}
}
IClientAPI[] localArray = m_array;
Parallel.ForEach<IClientAPI>(localArray, action);
}
// [Obsolete("Using Obsolete to drive development is invalid. Obsolete presumes that something new has already been created to replace this.")]
public uint[] GetAllCircuits(UUID agentId)
/// <summary>
/// Performs a given task synchronously for each of the elements in
/// the collection
/// </summary>
/// <param name="action">Action to perform on each element</param>
public void ForEachSync(Action<IClientAPI> action)
{
List<uint> circuits = new List<uint>();
// Wasteful, I know
IClientAPI[] LocalClients = new IClientAPI[0];
lock (m_clients)
{
LocalClients = new IClientAPI[m_clients.Count];
m_clients.Values.CopyTo(LocalClients, 0);
}
for (int i = 0; i < LocalClients.Length; i++)
{
if (LocalClients[i].AgentId == agentId)
{
circuits.Add(LocalClients[i].CircuitCode);
}
}
return circuits.ToArray();
}
public List<uint> GetAllCircuitCodes()
{
List<uint> circuits;
lock (m_clients)
{
circuits = new List<uint>(m_clients.Keys);
}
return circuits;
}
public void ViewerEffectHandler(IClientAPI sender, List<ViewerEffectEventHandlerArg> args)
{
// TODO: don't create new blocks if recycling an old packet
List<ViewerEffectPacket.EffectBlock> effectBlock = new List<ViewerEffectPacket.EffectBlock>();
for (int i = 0; i < args.Count; i++)
{
ViewerEffectPacket.EffectBlock effect = new ViewerEffectPacket.EffectBlock();
effect.AgentID = args[i].AgentID;
effect.Color = args[i].Color;
effect.Duration = args[i].Duration;
effect.ID = args[i].ID;
effect.Type = args[i].Type;
effect.TypeData = args[i].TypeData;
effectBlock.Add(effect);
}
ViewerEffectPacket.EffectBlock[] effectBlockArray = effectBlock.ToArray();
IClientAPI[] LocalClients;
lock (m_clients)
{
LocalClients = new IClientAPI[m_clients.Count];
m_clients.Values.CopyTo(LocalClients, 0);
}
for (int i = 0; i < LocalClients.Length; i++)
{
if (LocalClients[i].AgentId != sender.AgentId)
{
LocalClients[i].SendViewerEffect(effectBlockArray);
}
}
}
public bool TryGetClient(uint circuitId, out IClientAPI user)
{
lock (m_clients)
{
return m_clients.TryGetValue(circuitId, out user);
}
IClientAPI[] localArray = m_array;
for (int i = 0; i < localArray.Length; i++)
action(localArray[i]);
}
}
}

View File

@ -146,6 +146,7 @@ namespace OpenSim.Framework.Communications
// If the operation isn't done, wait for it
AsyncWaitHandle.WaitOne();
AsyncWaitHandle.Close();
m_waitHandle.Close();
m_waitHandle = null; // Allow early GC
}

View File

@ -105,7 +105,7 @@ namespace OpenSim.Framework.Communications
/// <summary>
/// This flag will help block the main synchroneous method, in case we run in synchroneous mode
/// </summary>
public static ManualResetEvent _allDone = new ManualResetEvent(false);
//public static ManualResetEvent _allDone = new ManualResetEvent(false);
/// <summary>
/// Default time out period
@ -282,12 +282,12 @@ namespace OpenSim.Framework.Communications
else
{
s.Close();
_allDone.Set();
//_allDone.Set();
}
}
catch (Exception e)
{
_allDone.Set();
//_allDone.Set();
_asyncException = e;
}
}

View File

@ -561,6 +561,8 @@ namespace OpenSim.Framework
// [Obsolete("LLClientView Specific - Circuits are unique to LLClientView")]
uint CircuitCode { get; }
IPEndPoint RemoteEndPoint { get; }
event GenericMessage OnGenericMessage;
// [Obsolete("LLClientView Specific - Replace with more bare-bones arguments.")]
@ -802,7 +804,7 @@ namespace OpenSim.Framework
void InPacket(object NewPack);
void ProcessInPacket(Packet NewPack);
void Close(bool ShutdownCircuit);
void Close();
void Kick(string message);
/// <summary>

View File

@ -71,7 +71,6 @@ namespace OpenSim.Framework
void AddNewClient(IClientAPI client);
void RemoveClient(UUID agentID);
void CloseAllAgents(uint circuitcode);
void Restart(int seconds);
//RegionInfo OtherRegionUp(RegionInfo thisRegion);

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2009, openmetaverse.org
* All rights reserved.
*
* - 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.
* - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Threading;
namespace OpenSim.Framework
{
public sealed class LocklessQueue<T>
{
private sealed class SingleLinkNode
{
public SingleLinkNode Next;
public T Item;
}
SingleLinkNode head;
SingleLinkNode tail;
int count;
public int Count { get { return count; } }
public LocklessQueue()
{
Init();
}
public void Enqueue(T item)
{
SingleLinkNode oldTail = null;
SingleLinkNode oldTailNext;
SingleLinkNode newNode = new SingleLinkNode();
newNode.Item = item;
bool newNodeWasAdded = false;
while (!newNodeWasAdded)
{
oldTail = tail;
oldTailNext = oldTail.Next;
if (tail == oldTail)
{
if (oldTailNext == null)
newNodeWasAdded = CAS(ref tail.Next, null, newNode);
else
CAS(ref tail, oldTail, oldTailNext);
}
}
CAS(ref tail, oldTail, newNode);
Interlocked.Increment(ref count);
}
public bool Dequeue(out T item)
{
item = default(T);
SingleLinkNode oldHead = null;
bool haveAdvancedHead = false;
while (!haveAdvancedHead)
{
oldHead = head;
SingleLinkNode oldTail = tail;
SingleLinkNode oldHeadNext = oldHead.Next;
if (oldHead == head)
{
if (oldHead == oldTail)
{
if (oldHeadNext == null)
return false;
CAS(ref tail, oldTail, oldHeadNext);
}
else
{
item = oldHeadNext.Item;
haveAdvancedHead = CAS(ref head, oldHead, oldHeadNext);
}
}
}
Interlocked.Decrement(ref count);
return true;
}
public void Clear()
{
Init();
}
private void Init()
{
count = 0;
head = tail = new SingleLinkNode();
}
private static bool CAS(ref SingleLinkNode location, SingleLinkNode comparand, SingleLinkNode newValue)
{
return
(object)comparand ==
(object)Interlocked.CompareExchange<SingleLinkNode>(ref location, newValue, comparand);
}
}
}

View File

@ -89,6 +89,7 @@ namespace OpenSim.Framework
}
threadFinishEvent.WaitOne();
threadFinishEvent.Close();
if (exception != null)
throw new Exception(exception.Message, exception);
@ -148,6 +149,7 @@ namespace OpenSim.Framework
}
threadFinishEvent.WaitOne();
threadFinishEvent.Close();
if (exception != null)
throw new Exception(exception.Message, exception);
@ -199,6 +201,7 @@ namespace OpenSim.Framework
}
threadFinishEvent.WaitOne();
threadFinishEvent.Close();
if (exception != null)
throw new Exception(exception.Message, exception);

View File

@ -31,13 +31,24 @@ namespace OpenSim.Framework
{
public enum ThrottleOutPacketType : int
{
Unknown = -1, // Also doubles as 'do not throttle'
/// <summary>Unthrottled packets</summary>
Unknown = -1,
/// <summary>Packets that are being resent</summary>
Resend = 0,
/// <summary>Terrain data</summary>
Land = 1,
/// <summary>Wind data</summary>
Wind = 2,
/// <summary>Cloud data</summary>
Cloud = 3,
/// <summary>Any packets that do not fit into the other throttles</summary>
Task = 4,
/// <summary>Texture assets</summary>
Texture = 5,
/// <summary>Non-texture assets</summary>
Asset = 6,
/// <summary>Avatar and primitive data</summary>
/// <remarks>This is a sub-category of Task</remarks>
State = 7,
}
}

View File

@ -239,6 +239,10 @@ namespace OpenSim
"show users [full]",
"Show user data", HandleShow);
m_console.Commands.AddCommand("region", false, "show connections",
"show connections",
"Show connection data", HandleShow);
m_console.Commands.AddCommand("region", false, "show users full",
"show users full",
String.Empty, HandleShow);
@ -921,7 +925,25 @@ namespace OpenSim
regionName));
}
m_log.Info("");
m_log.Info(String.Empty);
break;
case "connections":
System.Text.StringBuilder connections = new System.Text.StringBuilder("Connections:\n");
m_sceneManager.ForEachScene(
delegate(Scene scene)
{
scene.ClientManager.ForEachSync(
delegate(IClientAPI client)
{
connections.AppendFormat("{0}: {1} ({2}) from {3} on circuit {4}\n",
scene.RegionInfo.RegionName, client.Name, client.AgentId, client.RemoteEndPoint, client.CircuitCode);
}
);
}
);
m_log.Info(connections.ToString());
break;
case "modules":

View File

@ -58,6 +58,7 @@ namespace OpenSim
m_clientServers.Count.ToString(), m_clientServers.Count > 1 ? "s" : "");
WorldHasComeToAnEnd.WaitOne();
WorldHasComeToAnEnd.Close();
}
/// <summary>

View File

@ -31,6 +31,7 @@ using OpenMetaverse;
using OpenMetaverse.Imaging;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes.Hypergrid;
using OpenSim.Services.Interfaces;
using log4net;
using System.Reflection;
@ -54,6 +55,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public UUID TextureID;
public IJ2KDecoder J2KDecoder;
public IAssetService AssetService;
public UUID AgentID;
public IHyperAssetService HyperAssets;
public OpenJPEG.J2KLayerInfo[] Layers;
public bool IsDecoded;
public bool HasAsset;
@ -72,14 +75,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_imageManager = imageManager;
}
public bool SendPackets(LLClientView client, int maxpack)
/// <summary>
/// Sends packets for this texture to a client until packetsToSend is
/// hit or the transfer completes
/// </summary>
/// <param name="client">Reference to the client that the packets are destined for</param>
/// <param name="packetsToSend">Maximum number of packets to send during this call</param>
/// <param name="packetsSent">Number of packets sent during this call</param>
/// <returns>True if the transfer completes at the current discard level, otherwise false</returns>
public bool SendPackets(LLClientView client, int packetsToSend, out int packetsSent)
{
if (client == null)
return false;
packetsSent = 0;
if (m_currentPacket <= m_stopPacket)
{
int count = 0;
bool sendMore = true;
if (!m_sentInfo || (m_currentPacket == 0))
@ -88,25 +97,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_sentInfo = true;
++m_currentPacket;
++count;
++packetsSent;
}
if (m_currentPacket < 2)
{
m_currentPacket = 2;
}
while (sendMore && count < maxpack && m_currentPacket <= m_stopPacket)
while (sendMore && packetsSent < packetsToSend && m_currentPacket <= m_stopPacket)
{
sendMore = SendPacket(client);
++m_currentPacket;
++count;
++packetsSent;
}
if (m_currentPacket > m_stopPacket)
return true;
}
return false;
return (m_currentPacket > m_stopPacket);
}
public void RunUpdate()
@ -370,6 +376,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
UUID assetID = UUID.Zero;
if (asset != null)
assetID = asset.FullID;
else if ((HyperAssets != null) && (sender != HyperAssets))
{
// Try the user's inventory, but only if it's different from the regions'
string userAssets = HyperAssets.GetUserAssetServer(AgentID);
if ((userAssets != string.Empty) && (userAssets != HyperAssets.GetSimAssetServer()))
{
m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", id);
AssetService.Get(userAssets + "/" + id, HyperAssets, AssetReceived);
return;
}
}
AssetDataCallback(assetID, asset);

File diff suppressed because it is too large Load Diff

View File

@ -59,6 +59,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private C5.IntervalHeap<J2KImage> m_priorityQueue = new C5.IntervalHeap<J2KImage>(10, new J2KImageComparer());
private object m_syncRoot = new object();
private IHyperAssetService m_hyperAssets;
public LLClientView Client { get { return m_client; } }
public AssetBase MissingImage { get { return m_missingImage; } }
@ -74,6 +76,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_log.Error("[ClientView] - Couldn't set missing image asset, falling back to missing image packet. This is known to crash the client");
m_j2kDecodeModule = pJ2kDecodeModule;
m_hyperAssets = client.Scene.RequestModuleInterface<IHyperAssetService>();
}
/// <summary>
@ -146,6 +149,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
imgrequest = new J2KImage(this);
imgrequest.J2KDecoder = m_j2kDecodeModule;
imgrequest.AssetService = m_assetCache;
imgrequest.AgentID = m_client.AgentId;
imgrequest.HyperAssets = m_hyperAssets;
imgrequest.DiscardLevel = newRequest.DiscardLevel;
imgrequest.StartPacket = Math.Max(1, newRequest.PacketNumber);
imgrequest.Priority = newRequest.Priority;
@ -162,47 +167,48 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
public bool ProcessImageQueue(int count, int maxpack)
public bool ProcessImageQueue(int packetsToSend)
{
J2KImage imagereq;
int numCollected = 0;
m_lastloopprocessed = DateTime.Now.Ticks;
int packetsSent = 0;
//lock (m_syncRoot)
//{
m_lastloopprocessed = DateTime.Now.Ticks;
while (packetsSent < packetsToSend)
{
J2KImage image = GetHighestPriorityImage();
// This can happen during Close()
if (m_client == null)
// If null was returned, the texture priority queue is currently empty
if (image == null)
return false;
while ((imagereq = GetHighestPriorityImage()) != null)
if (image.IsDecoded)
{
if (imagereq.IsDecoded == true)
{
++numCollected;
int sent;
bool imageDone = image.SendPackets(m_client, packetsToSend - packetsSent, out sent);
packetsSent += sent;
if (imagereq.SendPackets(m_client, maxpack))
{
// Send complete. Destroy any knowledge of this transfer
RemoveImageFromQueue(imagereq);
}
}
if (numCollected == count)
break;
// If the send is complete, destroy any knowledge of this transfer
if (imageDone)
RemoveImageFromQueue(image);
}
//}
else
{
// TODO: This is a limitation of how LLImageManager is currently
// written. Undecoded textures should not be going into the priority
// queue, because a high priority undecoded texture will clog up the
// pipeline for a client
return true;
}
}
return m_priorityQueue.Count > 0;
}
//Faux destructor
/// <summary>
/// Faux destructor
/// </summary>
public void Close()
{
m_shuttingdown = true;
m_j2kDecodeModule = null;
m_assetCache = null;
m_client = null;
}
#region Priority Queue Helpers
@ -213,13 +219,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
lock (m_syncRoot)
{
if (m_priorityQueue.Count > 0)
{
try
{
image = m_priorityQueue.FindMax();
}
try { image = m_priorityQueue.FindMax(); }
catch (Exception) { }
}
}
@ -231,20 +233,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
image.PriorityQueueHandle = null;
lock (m_syncRoot)
try
{
m_priorityQueue.Add(ref image.PriorityQueueHandle, image);
}
try { m_priorityQueue.Add(ref image.PriorityQueueHandle, image); }
catch (Exception) { }
}
void RemoveImageFromQueue(J2KImage image)
{
lock (m_syncRoot)
try
{
m_priorityQueue.Delete(image.PriorityQueueHandle);
}
try { m_priorityQueue.Delete(image.PriorityQueueHandle); }
catch (Exception) { }
}

View File

@ -28,8 +28,10 @@
using System;
using System.Collections.Generic;
using System.Net;
using log4net;
using OpenSim.Framework;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace OpenSim.Region.ClientStack.LindenUDP
{
@ -59,9 +61,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </summary>
public sealed class LLUDPClient
{
// TODO: Make this a config setting
/// <summary>Percentage of the task throttle category that is allocated to avatar and prim
/// state updates</summary>
const float STATE_TASK_PERCENTAGE = 0.8f;
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>The number of packet categories to throttle on. If a throttle category is added
/// or removed, this number must also change</summary>
const int THROTTLE_CATEGORY_COUNT = 7;
const int THROTTLE_CATEGORY_COUNT = 8;
/// <summary>Fired when updated networking stats are produced for this client</summary>
public event PacketStats OnPacketStats;
@ -80,10 +89,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>Packets we have sent that need to be ACKed by the client</summary>
public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
/// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
public readonly LocklessQueue<uint> PendingAcks = new LocklessQueue<uint>();
public readonly OpenSim.Framework.LocklessQueue<uint> PendingAcks = new OpenSim.Framework.LocklessQueue<uint>();
/// <summary>Reference to the IClientAPI for this client</summary>
public LLClientView ClientAPI;
/// <summary>Current packet sequence number</summary>
public int CurrentSequence;
/// <summary>Current ping sequence number</summary>
@ -123,21 +130,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_packetsSentReported;
/// <summary>Throttle bucket for this agent's connection</summary>
private readonly TokenBucket throttle;
private readonly TokenBucket m_throttle;
/// <summary>Throttle buckets for each packet category</summary>
private readonly TokenBucket[] throttleCategories;
private readonly TokenBucket[] m_throttleCategories;
/// <summary>Throttle rate defaults and limits</summary>
private readonly ThrottleRates defaultThrottleRates;
private readonly ThrottleRates m_defaultThrottleRates;
/// <summary>Outgoing queues for throttled packets</summary>
private readonly LocklessQueue<OutgoingPacket>[] packetOutboxes = new LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
private readonly OpenSim.Framework.LocklessQueue<OutgoingPacket>[] m_packetOutboxes = new OpenSim.Framework.LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
/// <summary>A container that can hold one packet for each outbox, used to store
/// dequeued packets that are being held for throttling</summary>
private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
/// <summary>An optimization to store the length of dequeued packets being held
/// for throttling. This avoids expensive calls to Packet.Length</summary>
private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT];
private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
/// <summary>Flags to prevent queue empty callbacks from stacking up on
/// top of each other</summary>
private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT];
/// <summary>A reference to the LLUDPServer that is managing this client</summary>
private readonly LLUDPServer udpServer;
private readonly LLUDPServer m_udpServer;
/// <summary>
/// Default constructor
@ -151,24 +158,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="remoteEndPoint">Remote endpoint for this connection</param>
public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint)
{
udpServer = server;
AgentID = agentID;
RemoteEndPoint = remoteEndPoint;
CircuitCode = circuitCode;
defaultThrottleRates = rates;
m_udpServer = server;
m_defaultThrottleRates = rates;
m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total);
m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
packetOutboxes[i] = new LocklessQueue<OutgoingPacket>();
{
ThrottleOutPacketType type = (ThrottleOutPacketType)i;
throttle = new TokenBucket(parentThrottle, 0, 0);
throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend);
throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land);
throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind);
throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud);
throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit, rates.Task);
throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture);
throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset);
m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type));
}
// Set the granularity variable used for retransmission calculations to
// the measured resolution of Environment.TickCount
@ -176,6 +180,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Default the retransmission timeout to three seconds
RTO = 3000;
// Initialize this to a sane value to prevent early disconnects
TickLastPacketReceived = Environment.TickCount;
}
/// <summary>
@ -183,8 +190,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// </summary>
public void Shutdown()
{
// TODO: Do we need to invalidate the circuit?
IsConnected = false;
NeedAcks.Clear();
for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
{
m_packetOutboxes[i].Clear();
m_nextPackets[i] = null;
}
OnPacketStats = null;
OnQueueEmpty = null;
}
/// <summary>
@ -200,13 +214,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
info.pendingAcks = new Dictionary<uint, uint>();
info.needAck = new Dictionary<uint, byte[]>();
info.resendThrottle = throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
info.taskThrottle + info.assetThrottle + info.textureThrottle;
@ -267,6 +281,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
adjData = throttleData;
}
// 0.125f converts from bits to bytes
int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
@ -274,22 +289,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
// State is a subcategory of task that we allocate a percentage to
int state = (int)((float)task * STATE_TASK_PERCENTAGE);
task -= state;
resend = (resend <= defaultThrottleRates.ResendLimit) ? resend : defaultThrottleRates.ResendLimit;
land = (land <= defaultThrottleRates.LandLimit) ? land : defaultThrottleRates.LandLimit;
wind = (wind <= defaultThrottleRates.WindLimit) ? wind : defaultThrottleRates.WindLimit;
cloud = (cloud <= defaultThrottleRates.CloudLimit) ? cloud : defaultThrottleRates.CloudLimit;
task = (task <= defaultThrottleRates.TaskLimit) ? task : defaultThrottleRates.TaskLimit;
texture = (texture <= defaultThrottleRates.TextureLimit) ? texture : defaultThrottleRates.TextureLimit;
asset = (asset <= defaultThrottleRates.AssetLimit) ? asset : defaultThrottleRates.AssetLimit;
int ceiling = Int32.MaxValue;
if (m_defaultThrottleRates.Total != 0)
{
ceiling = m_defaultThrottleRates.Total;
if (ceiling < Packet.MTU) ceiling = Packet.MTU;
}
SetThrottle(ThrottleOutPacketType.Resend, resend);
SetThrottle(ThrottleOutPacketType.Land, land);
SetThrottle(ThrottleOutPacketType.Wind, wind);
SetThrottle(ThrottleOutPacketType.Cloud, cloud);
SetThrottle(ThrottleOutPacketType.Task, task);
SetThrottle(ThrottleOutPacketType.Texture, texture);
SetThrottle(ThrottleOutPacketType.Asset, asset);
resend = Utils.Clamp(resend, Packet.MTU, ceiling);
land = Utils.Clamp(land, Packet.MTU, ceiling);
wind = Utils.Clamp(wind, Packet.MTU, ceiling);
cloud = Utils.Clamp(cloud, Packet.MTU, ceiling);
task = Utils.Clamp(task, Packet.MTU, ceiling);
texture = Utils.Clamp(texture, Packet.MTU, ceiling);
asset = Utils.Clamp(asset, Packet.MTU, ceiling);
state = Utils.Clamp(state, Packet.MTU, ceiling);
int total = resend + land + wind + cloud + task + texture + asset + state;
int taskTotal = task + state;
m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}",
AgentID, resend, land, wind, cloud, task, texture, asset, state, total);
SetThrottle(ThrottleOutPacketType.Resend, resend, resend);
SetThrottle(ThrottleOutPacketType.Land, land, land);
SetThrottle(ThrottleOutPacketType.Wind, wind, wind);
SetThrottle(ThrottleOutPacketType.Cloud, cloud, cloud);
SetThrottle(ThrottleOutPacketType.Task, task, taskTotal);
SetThrottle(ThrottleOutPacketType.Texture, texture, texture);
SetThrottle(ThrottleOutPacketType.Asset, asset, asset);
SetThrottle(ThrottleOutPacketType.State, state, taskTotal);
}
public byte[] GetThrottlesPacked()
@ -297,25 +330,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
byte[] data = new byte[7 * 4];
int i = 0;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Task].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) +
m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
return data;
}
public void SetThrottle(ThrottleOutPacketType category, int rate)
public void SetThrottle(ThrottleOutPacketType category, int rate, int maxBurst)
{
int i = (int)category;
if (i >= 0 && i < throttleCategories.Length)
if (i >= 0 && i < m_throttleCategories.Length)
{
TokenBucket bucket = throttleCategories[(int)category];
bucket.MaxBurst = rate;
TokenBucket bucket = m_throttleCategories[(int)category];
bucket.DripRate = rate;
bucket.MaxBurst = maxBurst;
}
}
@ -323,12 +357,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
int category = (int)packet.Category;
if (category >= 0 && category < packetOutboxes.Length)
if (category >= 0 && category < m_packetOutboxes.Length)
{
LocklessQueue<OutgoingPacket> queue = packetOutboxes[category];
TokenBucket bucket = throttleCategories[category];
OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
TokenBucket bucket = m_throttleCategories[category];
if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
if (m_throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
{
// Enough tokens were removed from the bucket, the packet will not be queued
return false;
@ -357,24 +391,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public bool DequeueOutgoing()
{
OutgoingPacket packet;
LocklessQueue<OutgoingPacket> queue;
OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
TokenBucket bucket;
bool packetSent = false;
for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
{
bucket = throttleCategories[i];
bucket = m_throttleCategories[i];
if (nextPackets[i] != null)
if (m_nextPackets[i] != null)
{
// This bucket was empty the last time we tried to send a packet,
// leaving a dequeued packet still waiting to be sent out. Try to
// send it again
if (bucket.RemoveTokens(nextPacketLengths[i]))
OutgoingPacket nextPacket = m_nextPackets[i];
if (bucket.RemoveTokens(nextPacket.Buffer.DataLength))
{
// Send the packet
udpServer.SendPacketFinal(nextPackets[i]);
nextPackets[i] = null;
m_udpServer.SendPacketFinal(nextPacket);
m_nextPackets[i] = null;
packetSent = true;
}
}
@ -382,7 +417,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
// No dequeued packet waiting to be sent, try to pull one off
// this queue
queue = packetOutboxes[i];
queue = m_packetOutboxes[i];
if (queue.Dequeue(out packet))
{
// A packet was pulled off the queue. See if we have
@ -390,23 +425,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (bucket.RemoveTokens(packet.Buffer.DataLength))
{
// Send the packet
udpServer.SendPacketFinal(packet);
m_udpServer.SendPacketFinal(packet);
packetSent = true;
}
else
{
// Save the dequeued packet and the length calculation for
// the next iteration
nextPackets[i] = packet;
nextPacketLengths[i] = packet.Buffer.DataLength;
// Save the dequeued packet for the next iteration
m_nextPackets[i] = packet;
}
// If the queue is empty after this dequeue, fire the queue
// empty callback now so it has a chance to fill before we
// get back here
if (queue.Count == 0)
BeginFireQueueEmpty(i);
}
else
{
// No packets in this queue. Fire the queue empty callback
QueueEmpty callback = OnQueueEmpty;
if (callback != null)
callback((ThrottleOutPacketType)i);
// if it has not been called recently
BeginFireQueueEmpty(i);
}
}
}
@ -414,6 +452,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return packetSent;
}
/// <summary>
/// Called when an ACK packet is received and a round-trip time for a
/// packet is calculated. This is used to calculate the smoothed
/// round-trip time, round trip time variance, and finally the
/// retransmission timeout
/// </summary>
/// <param name="r">Round-trip time of a single packet and its
/// acknowledgement</param>
public void UpdateRoundTrip(float r)
{
const float ALPHA = 0.125f;
@ -435,8 +481,44 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Always round retransmission timeout up to two seconds
RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR)));
//Logger.Debug("Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " +
//m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " +
// RTTVAR + " based on new RTT of " + r + "ms");
}
/// <summary>
/// Does an early check to see if this queue empty callback is already
/// running, then asynchronously firing the event
/// </summary>
/// <param name="throttleIndex">Throttle category to fire the callback
/// for</param>
private void BeginFireQueueEmpty(int throttleIndex)
{
if (!m_onQueueEmptyRunning[throttleIndex])
Util.FireAndForget(FireQueueEmpty, throttleIndex);
}
/// <summary>
/// Checks to see if this queue empty callback is already running,
/// then firing the event
/// </summary>
/// <param name="o">Throttle category to fire the callback for, stored
/// as an object to match the WaitCallback delegate signature</param>
private void FireQueueEmpty(object o)
{
int i = (int)o;
ThrottleOutPacketType type = (ThrottleOutPacketType)i;
QueueEmpty callback = OnQueueEmpty;
if (callback != null)
{
if (!m_onQueueEmptyRunning[i])
{
m_onQueueEmptyRunning[i] = true;
try { callback(type); }
catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); }
m_onQueueEmptyRunning[i] = false;
}
}
}
}
}

View File

@ -1,282 +0,0 @@
/*
* 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 OpenSimulator 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 OpenSim.Framework;
using OpenMetaverse;
using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim;
namespace OpenSim.Region.ClientStack.LindenUDP
{
public sealed class UDPClientCollection
{
Dictionary<UUID, LLUDPClient> Dictionary1;
Dictionary<IPEndPoint, LLUDPClient> Dictionary2;
LLUDPClient[] Array;
ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl();
object m_sync = new object();
public UDPClientCollection()
{
Dictionary1 = new Dictionary<UUID, LLUDPClient>();
Dictionary2 = new Dictionary<IPEndPoint, LLUDPClient>();
Array = new LLUDPClient[0];
}
public UDPClientCollection(int capacity)
{
Dictionary1 = new Dictionary<UUID, LLUDPClient>(capacity);
Dictionary2 = new Dictionary<IPEndPoint, LLUDPClient>(capacity);
Array = new LLUDPClient[0];
}
public void Add(UUID key1, IPEndPoint key2, LLUDPClient value)
{
//rwLock.EnterWriteLock();
//try
//{
// if (Dictionary1.ContainsKey(key1))
// {
// if (!Dictionary2.ContainsKey(key2))
// throw new ArgumentException("key1 exists in the dictionary but not key2");
// }
// else if (Dictionary2.ContainsKey(key2))
// {
// if (!Dictionary1.ContainsKey(key1))
// throw new ArgumentException("key2 exists in the dictionary but not key1");
// }
// Dictionary1[key1] = value;
// Dictionary2[key2] = value;
// LLUDPClient[] oldArray = Array;
// int oldLength = oldArray.Length;
// LLUDPClient[] newArray = new LLUDPClient[oldLength + 1];
// for (int i = 0; i < oldLength; i++)
// newArray[i] = oldArray[i];
// newArray[oldLength] = value;
// Array = newArray;
//}
//finally { rwLock.ExitWriteLock(); }
lock (m_sync)
{
if (Dictionary1.ContainsKey(key1))
{
if (!Dictionary2.ContainsKey(key2))
throw new ArgumentException("key1 exists in the dictionary but not key2");
}
else if (Dictionary2.ContainsKey(key2))
{
if (!Dictionary1.ContainsKey(key1))
throw new ArgumentException("key2 exists in the dictionary but not key1");
}
Dictionary1[key1] = value;
Dictionary2[key2] = value;
LLUDPClient[] oldArray = Array;
int oldLength = oldArray.Length;
LLUDPClient[] newArray = new LLUDPClient[oldLength + 1];
for (int i = 0; i < oldLength; i++)
newArray[i] = oldArray[i];
newArray[oldLength] = value;
Array = newArray;
}
}
public bool Remove(UUID key1, IPEndPoint key2)
{
//rwLock.EnterWriteLock();
//try
//{
// LLUDPClient value;
// if (Dictionary1.TryGetValue(key1, out value))
// {
// Dictionary1.Remove(key1);
// Dictionary2.Remove(key2);
// LLUDPClient[] oldArray = Array;
// int oldLength = oldArray.Length;
// LLUDPClient[] newArray = new LLUDPClient[oldLength - 1];
// int j = 0;
// for (int i = 0; i < oldLength; i++)
// {
// if (oldArray[i] != value)
// newArray[j++] = oldArray[i];
// }
// Array = newArray;
// return true;
// }
//}
//finally { rwLock.ExitWriteLock(); }
//return false;
lock (m_sync)
{
LLUDPClient value;
if (Dictionary1.TryGetValue(key1, out value))
{
Dictionary1.Remove(key1);
Dictionary2.Remove(key2);
LLUDPClient[] oldArray = Array;
int oldLength = oldArray.Length;
LLUDPClient[] newArray = new LLUDPClient[oldLength - 1];
int j = 0;
for (int i = 0; i < oldLength; i++)
{
if (oldArray[i] != value)
newArray[j++] = oldArray[i];
}
Array = newArray;
return true;
}
}
return false;
}
public void Clear()
{
//rwLock.EnterWriteLock();
//try
//{
// Dictionary1.Clear();
// Dictionary2.Clear();
// Array = new LLUDPClient[0];
//}
//finally { rwLock.ExitWriteLock(); }
lock (m_sync)
{
Dictionary1.Clear();
Dictionary2.Clear();
Array = new LLUDPClient[0];
}
}
public int Count
{
get { return Array.Length; }
}
public bool ContainsKey(UUID key)
{
return Dictionary1.ContainsKey(key);
}
public bool ContainsKey(IPEndPoint key)
{
return Dictionary2.ContainsKey(key);
}
public bool TryGetValue(UUID key, out LLUDPClient value)
{
////bool success;
////bool doLock = !rwLock.IsUpgradeableReadLockHeld;
////if (doLock) rwLock.EnterReadLock();
////try { success = Dictionary1.TryGetValue(key, out value); }
////finally { if (doLock) rwLock.ExitReadLock(); }
////return success;
lock (m_sync)
return Dictionary1.TryGetValue(key, out value);
//try
//{
// return Dictionary1.TryGetValue(key, out value);
//}
//catch { }
//value = null;
//return false;
}
public bool TryGetValue(IPEndPoint key, out LLUDPClient value)
{
////bool success;
////bool doLock = !rwLock.IsUpgradeableReadLockHeld;
////if (doLock) rwLock.EnterReadLock();
////try { success = Dictionary2.TryGetValue(key, out value); }
////finally { if (doLock) rwLock.ExitReadLock(); }
////return success;
lock (m_sync)
return Dictionary2.TryGetValue(key, out value);
//try
//{
// return Dictionary2.TryGetValue(key, out value);
//}
//catch { }
//value = null;
//return false;
}
public void ForEach(Action<LLUDPClient> action)
{
//bool doLock = !rwLock.IsUpgradeableReadLockHeld;
//if (doLock) rwLock.EnterUpgradeableReadLock();
//try { Parallel.ForEach<LLUDPClient>(Array, action); }
//finally { if (doLock) rwLock.ExitUpgradeableReadLock(); }
LLUDPClient[] localArray = null;
lock (m_sync)
{
localArray = new LLUDPClient[Array.Length];
Array.CopyTo(localArray, 0);
}
Parallel.ForEach<LLUDPClient>(localArray, action);
}
}
}

View File

@ -96,7 +96,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary>Incoming packets that are awaiting handling</summary>
private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
/// <summary></summary>
private UDPClientCollection clients = new UDPClientCollection();
//private UDPClientCollection m_clients = new UDPClientCollection();
/// <summary>Bandwidth throttle for this UDP server</summary>
private TokenBucket m_throttle;
/// <summary>Bandwidth throttle rates for this UDP server</summary>
@ -109,13 +109,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private Location m_location;
/// <summary>The measured resolution of Environment.TickCount</summary>
private float m_tickCountResolution;
/// <summary>The size of the receive buffer for the UDP socket. This value
/// is passed up to the operating system and used in the system networking
/// stack. Use zero to leave this value as the default</summary>
private int m_recvBufferSize;
/// <summary>Flag to process packets asynchronously or synchronously</summary>
private bool m_asyncPacketHandling;
/// <summary>The measured resolution of Environment.TickCount</summary>
public float TickCountResolution { get { return m_tickCountResolution; } }
public Socket Server { get { return null; } }
public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
: base((int)port)
: base(listenIP, (int)port)
{
#region Environment.TickCount Measurement
@ -134,18 +140,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion Environment.TickCount Measurement
m_circuitManager = circuitManager;
int sceneThrottleBps = 0;
// TODO: Config support for throttling the entire connection
m_throttle = new TokenBucket(null, 0, 0);
IConfig config = configSource.Configs["ClientStack.LindenUDP"];
if (config != null)
{
m_asyncPacketHandling = config.GetBoolean("async_packet_handling", false);
m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0);
sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0);
}
m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps);
m_throttleRates = new ThrottleRates(configSource);
}
public new void Start()
public void Start()
{
if (m_scene == null)
throw new InvalidOperationException("Cannot LLUDPServer.Start() without an IScene reference");
throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference");
base.Start();
m_log.Info("[LLUDPSERVER]: Starting the LLUDP server in " + (m_asyncPacketHandling ? "asynchronous" : "synchronous") + " mode");
base.Start(m_recvBufferSize, m_asyncPacketHandling);
// Start the incoming packet processing thread
Thread incomingThread = new Thread(IncomingPacketHandler);
@ -181,24 +197,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return x == m_location;
}
public void RemoveClient(IClientAPI client)
{
m_scene.ClientManager.Remove(client.CircuitCode);
client.Close(false);
LLUDPClient udpClient;
if (clients.TryGetValue(client.AgentId, out udpClient))
{
m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + client.Name + " in " + m_scene.RegionInfo.RegionName);
udpClient.Shutdown();
clients.Remove(client.AgentId, udpClient.RemoteEndPoint);
}
else
{
m_log.Warn("[LLUDPSERVER]: Failed to remove LLUDPClient for " + client.Name);
}
}
public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
{
// CoarseLocationUpdate packets cannot be split in an automated way
@ -216,30 +214,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
for (int i = 0; i < packetCount; i++)
{
byte[] data = datas[i];
clients.ForEach(
delegate(LLUDPClient client)
{ SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); });
m_scene.ClientManager.ForEach(
delegate(IClientAPI client)
{
if (client is LLClientView)
SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category);
}
);
}
}
else
{
byte[] data = packet.ToBytes();
clients.ForEach(
delegate(LLUDPClient client)
{ SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); });
m_scene.ClientManager.ForEach(
delegate(IClientAPI client)
{
if (client is LLClientView)
SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category);
}
);
}
}
public void SendPacket(UUID agentID, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
{
LLUDPClient client;
if (clients.TryGetValue(agentID, out client))
SendPacket(client, packet, category, allowSplitting);
else
m_log.Warn("[LLUDPSERVER]: Attempted to send a packet to unknown agentID " + agentID);
}
public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
{
// CoarseLocationUpdate packets cannot be split in an automated way
if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
@ -256,25 +253,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
for (int i = 0; i < packetCount; i++)
{
byte[] data = datas[i];
SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category);
SendPacketData(udpClient, data, packet.Type, category);
}
}
else
{
byte[] data = packet.ToBytes();
SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category);
SendPacketData(udpClient, data, packet.Type, category);
}
}
public void SendPacketData(LLUDPClient client, byte[] data, int dataLength, PacketType type, bool doZerocode, ThrottleOutPacketType category)
public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category)
{
int dataLength = data.Length;
bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0;
// Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum.
// The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting
// there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here
// to accomodate for both common scenarios and provide ample room for ACK appending in both
int bufferSize = (dataLength > 180) ? Packet.MTU : 200;
UDPPacketBuffer buffer = new UDPPacketBuffer(client.RemoteEndPoint, bufferSize);
UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize);
// Zerocode if needed
if (doZerocode)
@ -285,17 +285,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// The packet grew larger than the bufferSize while zerocoding.
// Remove the MSG_ZEROCODED flag and send the unencoded data
// instead
m_log.Info("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding. Removing MSG_ZEROCODED flag");
m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". Removing MSG_ZEROCODED flag");
data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED);
//
buffer = new UDPPacketBuffer(client.RemoteEndPoint, dataLength);
//
Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
}
}
else
{
// ??? will it fit?
Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
}
buffer.DataLength = dataLength;
@ -303,7 +299,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region Queue or Send
// Look up the UDPClient this is going to
OutgoingPacket outgoingPacket = new OutgoingPacket(client, buffer, category);
OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category);
if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
SendPacketFinal(outgoingPacket);
@ -311,18 +307,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion Queue or Send
}
public void SendAcks(LLUDPClient client)
public void SendAcks(LLUDPClient udpClient)
{
uint ack;
if (client.PendingAcks.Dequeue(out ack))
if (udpClient.PendingAcks.Dequeue(out ack))
{
List<PacketAckPacket.PacketsBlock> blocks = new List<PacketAckPacket.PacketsBlock>();
PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock();
block.ID = ack;
blocks.Add(block);
while (client.PendingAcks.Dequeue(out ack))
while (udpClient.PendingAcks.Dequeue(out ack))
{
block = new PacketAckPacket.PacketsBlock();
block.ID = ack;
@ -333,22 +329,39 @@ namespace OpenSim.Region.ClientStack.LindenUDP
packet.Header.Reliable = false;
packet.Packets = blocks.ToArray();
SendPacket(client, packet, ThrottleOutPacketType.Unknown, true);
SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true);
}
}
public void SendPing(LLUDPClient client)
public void SendPing(LLUDPClient udpClient)
{
IClientAPI api = client.ClientAPI;
if (api != null)
api.SendStartPingCheck(client.CurrentPingSequence++);
StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck);
pc.Header.Reliable = false;
OutgoingPacket oldestPacket = udpClient.NeedAcks.GetOldest();
pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++;
pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0;
SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false);
}
public void ResendUnacked(LLUDPClient client)
public void ResendUnacked(LLUDPClient udpClient)
{
if (client.NeedAcks.Count > 0)
if (udpClient.IsConnected && udpClient.NeedAcks.Count > 0)
{
List<OutgoingPacket> expiredPackets = client.NeedAcks.GetExpiredPackets(client.RTO);
// Disconnect an agent if no packets are received for some time
//FIXME: Make 60 an .ini setting
if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60)
{
m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID);
RemoveClient(udpClient);
return;
}
// Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO
List<OutgoingPacket> expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO);
if (expiredPackets != null)
{
@ -357,54 +370,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
OutgoingPacket outgoingPacket = expiredPackets[i];
// FIXME: Make this an .ini setting
if (outgoingPacket.ResendCount < 3)
{
//Logger.Debug(String.Format("Resending packet #{0} (attempt {1}), {2}ms have passed",
// outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount));
//m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
// outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
// Set the resent flag
outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
outgoingPacket.Category = ThrottleOutPacketType.Resend;
// Set the resent flag
outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
outgoingPacket.Category = ThrottleOutPacketType.Resend;
// The TickCount will be set to the current time when the packet
// is actually sent out again
outgoingPacket.TickCount = 0;
// The TickCount will be set to the current time when the packet
// is actually sent out again
outgoingPacket.TickCount = 0;
// Bump up the resend count on this packet
Interlocked.Increment(ref outgoingPacket.ResendCount);
//Interlocked.Increment(ref Stats.ResentPackets);
// Bump up the resend count on this packet
Interlocked.Increment(ref outgoingPacket.ResendCount);
//Interlocked.Increment(ref Stats.ResentPackets);
// Queue or (re)send the packet
if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
SendPacketFinal(outgoingPacket);
}
else
{
m_log.DebugFormat("[LLUDPSERVER]: Dropping packet #{0} for agent {1} after {2} failed attempts",
outgoingPacket.SequenceNumber, outgoingPacket.Client.RemoteEndPoint, outgoingPacket.ResendCount);
lock (client.NeedAcks.SyncRoot)
client.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber);
//Interlocked.Increment(ref Stats.DroppedPackets);
// Disconnect an agent if no packets are received for some time
//FIXME: Make 60 an .ini setting
if (Environment.TickCount - client.TickLastPacketReceived > 1000 * 60)
{
m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name);
RemoveClient(client.ClientAPI);
return;
}
}
// Requeue or resend the packet
if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
SendPacketFinal(outgoingPacket);
}
}
}
}
public void Flush()
public void Flush(LLUDPClient udpClient)
{
// FIXME: Implement?
}
@ -415,7 +404,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
byte flags = buffer.Data[0];
bool isResend = (flags & Helpers.MSG_RESENT) != 0;
bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0;
LLUDPClient client = outgoingPacket.Client;
LLUDPClient udpClient = outgoingPacket.Client;
if (!udpClient.IsConnected)
return;
// Keep track of when this packet was sent out (right now)
outgoingPacket.TickCount = Environment.TickCount;
@ -424,11 +416,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
int dataLength = buffer.DataLength;
// Keep appending ACKs until there is no room left in the packet or there are
// Keep appending ACKs until there is no room left in the buffer or there are
// no more ACKs to append
uint ackCount = 0;
uint ack;
while (dataLength + 5 < buffer.Data.Length && client.PendingAcks.Dequeue(out ack))
while (dataLength + 5 < buffer.Data.Length && udpClient.PendingAcks.Dequeue(out ack))
{
Utils.UIntToBytesBig(ack, buffer.Data, dataLength);
dataLength += 4;
@ -447,24 +439,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion ACK Appending
#region Sequence Number Assignment
if (!isResend)
{
// Not a resend, assign a new sequence number
uint sequenceNumber = (uint)Interlocked.Increment(ref client.CurrentSequence);
uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence);
Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1);
outgoingPacket.SequenceNumber = sequenceNumber;
if (isReliable)
{
// Add this packet to the list of ACK responses we are waiting on from the server
client.NeedAcks.Add(outgoingPacket);
udpClient.NeedAcks.Add(outgoingPacket);
}
}
#endregion Sequence Number Assignment
// Stats tracking
Interlocked.Increment(ref client.PacketsSent);
Interlocked.Increment(ref udpClient.PacketsSent);
if (isReliable)
Interlocked.Add(ref client.UnackedBytes, outgoingPacket.Buffer.DataLength);
Interlocked.Add(ref udpClient.UnackedBytes, outgoingPacket.Buffer.DataLength);
// Put the UDP payload on the wire
AsyncBeginSend(buffer);
@ -473,10 +469,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
protected override void PacketReceived(UDPPacketBuffer buffer)
{
// Debugging/Profiling
//try { Thread.CurrentThread.Name = "PacketReceived (" + scene.RegionName + ")"; }
//try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; }
//catch (Exception) { }
LLUDPClient client = null;
LLUDPClient udpClient = null;
Packet packet = null;
int packetEnd = buffer.DataLength - 1;
IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
@ -491,61 +487,59 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
catch (MalformedDataException)
{
m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet:\n{0}",
Utils.BytesToHexString(buffer.Data, buffer.DataLength, null));
m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet from {0}:\n{1}",
buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null));
}
// Fail-safe check
if (packet == null)
{
m_log.Warn("[LLUDPSERVER]: Couldn't build a message from the incoming data");
m_log.Warn("[LLUDPSERVER]: Couldn't build a message from incoming data " + buffer.DataLength +
" bytes long from " + buffer.RemoteEndPoint);
return;
}
//Stats.RecvBytes += (ulong)buffer.DataLength;
//++Stats.RecvPackets;
#endregion Decoding
#region UseCircuitCode Handling
#region Packet to Client Mapping
// UseCircuitCode handling
if (packet.Type == PacketType.UseCircuitCode)
{
UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet;
IClientAPI newuser;
uint circuitCode = useCircuitCode.CircuitCode.Code;
// Check if the client is already established
if (!m_scene.ClientManager.TryGetClient(circuitCode, out newuser))
{
AddNewClient(useCircuitCode, (IPEndPoint)buffer.RemoteEndPoint);
}
AddNewClient((UseCircuitCodePacket)packet, (IPEndPoint)buffer.RemoteEndPoint);
}
// Determine which agent this packet came from
if (!clients.TryGetValue(address, out client))
IClientAPI client;
if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView))
{
m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName);
m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address +
" in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients");
return;
}
#endregion UseCircuitCode Handling
udpClient = ((LLClientView)client).UDPClient;
if (!udpClient.IsConnected)
return;
#endregion Packet to Client Mapping
// Stats tracking
Interlocked.Increment(ref client.PacketsReceived);
Interlocked.Increment(ref udpClient.PacketsReceived);
#region ACK Receiving
int now = Environment.TickCount;
client.TickLastPacketReceived = now;
udpClient.TickLastPacketReceived = now;
// Handle appended ACKs
if (packet.Header.AppendedAcks && packet.Header.AckList != null)
{
lock (client.NeedAcks.SyncRoot)
lock (udpClient.NeedAcks.SyncRoot)
{
for (int i = 0; i < packet.Header.AckList.Length; i++)
AcknowledgePacket(client, packet.Header.AckList[i], now, packet.Header.Resent);
AcknowledgePacket(udpClient, packet.Header.AckList[i], now, packet.Header.Resent);
}
}
@ -554,10 +548,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
PacketAckPacket ackPacket = (PacketAckPacket)packet;
lock (client.NeedAcks.SyncRoot)
lock (udpClient.NeedAcks.SyncRoot)
{
for (int i = 0; i < ackPacket.Packets.Length; i++)
AcknowledgePacket(client, ackPacket.Packets[i].ID, now, packet.Header.Resent);
AcknowledgePacket(udpClient, ackPacket.Packets[i].ID, now, packet.Header.Resent);
}
}
@ -566,27 +560,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region ACK Sending
if (packet.Header.Reliable)
client.PendingAcks.Enqueue((uint)packet.Header.Sequence);
udpClient.PendingAcks.Enqueue(packet.Header.Sequence);
// This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out,
// add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove
// 2*MTU bytes from the value and send ACKs, and finally add the local value back to
// client.BytesSinceLastACK. Lockless thread safety
int bytesSinceLastACK = Interlocked.Exchange(ref client.BytesSinceLastACK, 0);
int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0);
bytesSinceLastACK += buffer.DataLength;
if (bytesSinceLastACK > Packet.MTU * 2)
{
bytesSinceLastACK -= Packet.MTU * 2;
SendAcks(client);
SendAcks(udpClient);
}
Interlocked.Add(ref client.BytesSinceLastACK, bytesSinceLastACK);
Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK);
#endregion ACK Sending
#region Incoming Packet Accounting
// Check the archive of received reliable packet IDs to see whether we already received this packet
if (packet.Header.Reliable && !client.PacketArchive.TryEnqueue(packet.Header.Sequence))
if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence))
{
if (packet.Header.Resent)
m_log.Debug("[LLUDPSERVER]: Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type);
@ -603,7 +597,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (packet.Type != PacketType.PacketAck)
{
// Inbox insertion
packetInbox.Enqueue(new IncomingPacket(client, packet));
packetInbox.Enqueue(new IncomingPacket(udpClient, packet));
}
}
@ -623,53 +617,59 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint)
{
//Slave regions don't accept new clients
UUID agentID = useCircuitCode.CircuitCode.ID;
UUID sessionID = useCircuitCode.CircuitCode.SessionID;
uint circuitCode = useCircuitCode.CircuitCode.Code;
if (m_scene.RegionStatus != RegionStatus.SlaveScene)
{
AuthenticateResponse sessionInfo;
bool isNewCircuit = !clients.ContainsKey(remoteEndPoint);
if (!IsClientAuthorized(useCircuitCode, out sessionInfo))
if (IsClientAuthorized(useCircuitCode, out sessionInfo))
{
m_log.WarnFormat(
"[CONNECTION FAILURE]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}",
useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint);
return;
}
if (isNewCircuit)
{
UUID agentID = useCircuitCode.CircuitCode.ID;
UUID sessionID = useCircuitCode.CircuitCode.SessionID;
uint circuitCode = useCircuitCode.CircuitCode.Code;
AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo);
}
else
{
// Don't create circuits for unauthorized clients
m_log.WarnFormat(
"[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}",
useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint);
}
}
else
{
// Slave regions don't accept new clients
m_log.Debug("[LLUDPSERVER]: Slave region " + m_scene.RegionInfo.RegionName + " ignoring UseCircuitCode packet");
}
}
private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo)
{
// Create the LLUDPClient
LLUDPClient client = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint);
LLUDPClient udpClient = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint);
// Create the LLClientView
LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode);
clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler;
clientApi.OnLogout += LogoutHandler;
clientApi.OnConnectionClosed += RemoveClient;
if (!m_scene.ClientManager.ContainsKey(agentID))
{
// Create the LLClientView
LLClientView client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
client.OnLogout += LogoutHandler;
// Start the IClientAPI
m_scene.ClientManager.Add(circuitCode, clientApi);
clientApi.Start();
// Start the IClientAPI
client.Start();
}
else
{
m_log.WarnFormat("[LLUDPSERVER]: Ignoring a repeated UseCircuitCode from {0} at {1} for circuit {2}",
udpClient.AgentID, remoteEndPoint, circuitCode);
}
}
// Give LLUDPClient a reference to IClientAPI
client.ClientAPI = clientApi;
// Add the new client to our list of tracked clients
clients.Add(agentID, client.RemoteEndPoint, client);
m_log.DebugFormat("[LLUDPSERVER]: Added new client {0} to region {1}", agentID, m_scene.RegionInfo.RegionName);
private void RemoveClient(LLUDPClient udpClient)
{
// Remove this client from the scene
IClientAPI client;
if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client))
client.Close();
}
private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend)
@ -747,20 +747,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
elapsed500MS = 0;
}
clients.ForEach(
delegate(LLUDPClient client)
m_scene.ClientManager.ForEach(
delegate(IClientAPI client)
{
if (client.DequeueOutgoing())
packetSent = true;
if (resendUnacked)
ResendUnacked(client);
if (sendAcks)
if (client is LLClientView)
{
SendAcks(client);
client.SendPacketStats();
LLUDPClient udpClient = ((LLClientView)client).UDPClient;
if (udpClient.IsConnected)
{
if (udpClient.DequeueOutgoing())
packetSent = true;
if (resendUnacked)
ResendUnacked(udpClient);
if (sendAcks)
{
SendAcks(udpClient);
udpClient.SendPacketStats();
}
if (sendPings)
SendPing(udpClient);
}
}
if (sendPings)
SendPing(client);
}
);
@ -773,38 +781,48 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
IncomingPacket incomingPacket = (IncomingPacket)state;
Packet packet = incomingPacket.Packet;
LLUDPClient client = incomingPacket.Client;
LLUDPClient udpClient = incomingPacket.Client;
IClientAPI client;
// Sanity check
if (packet == null || client == null || client.ClientAPI == null)
if (packet == null || udpClient == null)
{
m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", Client=\"{1}\", Client.ClientAPI=\"{2}\"",
packet, client, (client != null) ? client.ClientAPI : null);
m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", UDPClient=\"{1}\"",
packet, udpClient);
}
try
// Make sure this client is still alive
if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client))
{
// Process this packet
client.ClientAPI.ProcessInPacket(packet);
try
{
// Process this packet
client.ProcessInPacket(packet);
}
catch (ThreadAbortException)
{
// If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down
m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server");
Stop();
}
catch (Exception e)
{
// Don't let a failure in an individual client thread crash the whole sim.
m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type);
m_log.Error(e.Message, e);
}
}
catch (ThreadAbortException)
else
{
// If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down
m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server");
Stop();
}
catch (Exception e)
{
// Don't let a failure in an individual client thread crash the whole sim.
m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", client.AgentID, packet.Type);
m_log.Error(e.Message, e);
m_log.DebugFormat("[LLUDPSERVER]: Dropping incoming {0} packet for dead client {1}", packet.Type, udpClient.AgentID);
}
}
private void LogoutHandler(IClientAPI client)
{
client.SendLogoutPacket();
RemoveClient(client);
if (client.IsActive)
RemoveClient(((LLClientView)client).UDPClient);
}
}
}

View File

@ -1,419 +1,137 @@
//--------- Modified Version -------------------
///*
// * Copyright (c) 2006, Clutch, Inc.
// * Original Author: Jeff Cesnik
// * All rights reserved.
// *
// * - 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.
// * - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.
// */
/*
* Copyright (c) 2006, Clutch, Inc.
* Original Author: Jeff Cesnik
* All rights reserved.
*
* - 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.
* - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using OpenMetaverse;
using log4net;
//namespace OpenSim.Region.ClientStack.LindenUDP
//{
// /// <summary>
// ///
// /// </summary>
// public abstract class OpenSimUDPBase
// {
// // these abstract methods must be implemented in a derived class to actually do
// // something with the packets that are sent and received.
// protected abstract void PacketReceived(UDPPacketBuffer buffer);
// protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent);
// // the port to listen on
// internal int udpPort;
// // the UDP socket
// private Socket udpSocket;
// // the ReaderWriterLock is used solely for the purposes of shutdown (Stop()).
// // since there are potentially many "reader" threads in the internal .NET IOCP
// // thread pool, this is a cheaper synchronization primitive than using
// // a Mutex object. This allows many UDP socket "reads" concurrently - when
// // Stop() is called, it attempts to obtain a writer lock which will then
// // wait until all outstanding operations are completed before shutting down.
// // this avoids the problem of closing the socket with outstanding operations
// // and trying to catch the inevitable ObjectDisposedException.
// private ReaderWriterLock rwLock = new ReaderWriterLock();
// // number of outstanding operations. This is a reference count
// // which we use to ensure that the threads exit cleanly. Note that
// // we need this because the threads will potentially still need to process
// // data even after the socket is closed.
// private int rwOperationCount = 0;
// // the all important shutdownFlag. This is synchronized through the ReaderWriterLock.
// private volatile bool shutdownFlag = true;
// // the remote endpoint to communicate with
// protected IPEndPoint remoteEndPoint = null;
// /// <summary>
// /// Initialize the UDP packet handler in server mode
// /// </summary>
// /// <param name="port">Port to listening for incoming UDP packets on</param>
// public OpenSimUDPBase(int port)
// {
// udpPort = port;
// }
// /// <summary>
// /// Initialize the UDP packet handler in client mode
// /// </summary>
// /// <param name="endPoint">Remote UDP server to connect to</param>
// public OpenSimUDPBase(IPEndPoint endPoint)
// {
// remoteEndPoint = endPoint;
// udpPort = 0;
// }
// /// <summary>
// ///
// /// </summary>
// public void Start()
// {
// if (shutdownFlag)
// {
// if (remoteEndPoint == null)
// {
// // Server mode
// // create and bind the socket
// IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort);
// udpSocket = new Socket(
// AddressFamily.InterNetwork,
// SocketType.Dgram,
// ProtocolType.Udp);
// udpSocket.Bind(ipep);
// }
// else
// {
// // Client mode
// IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort);
// udpSocket = new Socket(
// AddressFamily.InterNetwork,
// SocketType.Dgram,
// ProtocolType.Udp);
// udpSocket.Bind(ipep);
// //udpSocket.Connect(remoteEndPoint);
// }
// // we're not shutting down, we're starting up
// shutdownFlag = false;
// // kick off an async receive. The Start() method will return, the
// // actual receives will occur asynchronously and will be caught in
// // AsyncEndRecieve().
// AsyncBeginReceive();
// }
// }
// /// <summary>
// ///
// /// </summary>
// public void Stop()
// {
// if (!shutdownFlag)
// {
// // wait indefinitely for a writer lock. Once this is called, the .NET runtime
// // will deny any more reader locks, in effect blocking all other send/receive
// // threads. Once we have the lock, we set shutdownFlag to inform the other
// // threads that the socket is closed.
// rwLock.AcquireWriterLock(-1);
// shutdownFlag = true;
// udpSocket.Close();
// rwLock.ReleaseWriterLock();
// // wait for any pending operations to complete on other
// // threads before exiting.
// const int FORCE_STOP = 100;
// int i = 0;
// while (rwOperationCount > 0 && i < FORCE_STOP)
// {
// Thread.Sleep(10);
// ++i;
// }
// if (i >= FORCE_STOP)
// {
// Logger.Log("UDPBase.Stop() forced shutdown while waiting on pending operations",
// Helpers.LogLevel.Warning);
// }
// }
// }
// /// <summary>
// ///
// /// </summary>
// public bool IsRunning
// {
// get { return !shutdownFlag; }
// }
// private void AsyncBeginReceive()
// {
// // this method actually kicks off the async read on the socket.
// // we aquire a reader lock here to ensure that no other thread
// // is trying to set shutdownFlag and close the socket.
// rwLock.AcquireReaderLock(-1);
// if (!shutdownFlag)
// {
// // increment the count of pending operations
// Interlocked.Increment(ref rwOperationCount);
// // allocate a packet buffer
// //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
// UDPPacketBuffer buf = new UDPPacketBuffer();
// try
// {
// // kick off an async read
// udpSocket.BeginReceiveFrom(
// //wrappedBuffer.Instance.Data,
// buf.Data,
// 0,
// UDPPacketBuffer.BUFFER_SIZE,
// SocketFlags.None,
// //ref wrappedBuffer.Instance.RemoteEndPoint,
// ref buf.RemoteEndPoint,
// new AsyncCallback(AsyncEndReceive),
// //wrappedBuffer);
// buf);
// }
// catch (SocketException)
// {
// // something bad happened
// //Logger.Log(
// // "A SocketException occurred in UDPServer.AsyncBeginReceive()",
// // Helpers.LogLevel.Error, se);
// // an error occurred, therefore the operation is void. Decrement the reference count.
// Interlocked.Decrement(ref rwOperationCount);
// }
// }
// // we're done with the socket for now, release the reader lock.
// rwLock.ReleaseReaderLock();
// }
// private void AsyncEndReceive(IAsyncResult iar)
// {
// // Asynchronous receive operations will complete here through the call
// // to AsyncBeginReceive
// // aquire a reader lock
// rwLock.AcquireReaderLock(-1);
// if (!shutdownFlag)
// {
// // get the buffer that was created in AsyncBeginReceive
// // this is the received data
// //WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState;
// //UDPPacketBuffer buffer = wrappedBuffer.Instance;
// UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
// try
// {
// // get the length of data actually read from the socket, store it with the
// // buffer
// buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
// // this operation is now complete, decrement the reference count
// Interlocked.Decrement(ref rwOperationCount);
// // we're done with the socket, release the reader lock
// rwLock.ReleaseReaderLock();
// // call the abstract method PacketReceived(), passing the buffer that
// // has just been filled from the socket read.
// PacketReceived(buffer);
// }
// catch (SocketException)
// {
// // an error occurred, therefore the operation is void. Decrement the reference count.
// Interlocked.Decrement(ref rwOperationCount);
// // we're done with the socket for now, release the reader lock.
// rwLock.ReleaseReaderLock();
// }
// finally
// {
// // start another receive - this keeps the server going!
// AsyncBeginReceive();
// //wrappedBuffer.Dispose();
// }
// }
// else
// {
// // nothing bad happened, but we are done with the operation
// // decrement the reference count and release the reader lock
// Interlocked.Decrement(ref rwOperationCount);
// rwLock.ReleaseReaderLock();
// }
// }
// public void AsyncBeginSend(UDPPacketBuffer buf)
// {
// rwLock.AcquireReaderLock(-1);
// if (!shutdownFlag)
// {
// try
// {
// Interlocked.Increment(ref rwOperationCount);
// udpSocket.BeginSendTo(
// buf.Data,
// 0,
// buf.DataLength,
// SocketFlags.None,
// buf.RemoteEndPoint,
// new AsyncCallback(AsyncEndSend),
// buf);
// }
// catch (SocketException)
// {
// //Logger.Log(
// // "A SocketException occurred in UDPServer.AsyncBeginSend()",
// // Helpers.LogLevel.Error, se);
// }
// }
// rwLock.ReleaseReaderLock();
// }
// private void AsyncEndSend(IAsyncResult iar)
// {
// rwLock.AcquireReaderLock(-1);
// if (!shutdownFlag)
// {
// UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
// try
// {
// int bytesSent = udpSocket.EndSendTo(iar);
// // note that call to the abstract PacketSent() method - we are passing the number
// // of bytes sent in a separate parameter, since we can't use buffer.DataLength which
// // is the number of bytes to send (or bytes received depending upon whether this
// // buffer was part of a send or a receive).
// PacketSent(buffer, bytesSent);
// }
// catch (SocketException)
// {
// //Logger.Log(
// // "A SocketException occurred in UDPServer.AsyncEndSend()",
// // Helpers.LogLevel.Error, se);
// }
// }
// Interlocked.Decrement(ref rwOperationCount);
// rwLock.ReleaseReaderLock();
// }
// }
//}
//--------- Original Version -------------------
namespace OpenSim.Region.ClientStack.LindenUDP
namespace OpenMetaverse
{
/// <summary>
///
/// Base UDP server
/// </summary>
public abstract class OpenSimUDPBase
{
// these abstract methods must be implemented in a derived class to actually do
// something with the packets that are sent and received.
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// This method is called when an incoming packet is received
/// </summary>
/// <param name="buffer">Incoming packet buffer</param>
protected abstract void PacketReceived(UDPPacketBuffer buffer);
/// <summary>
/// This method is called when an outgoing packet is sent
/// </summary>
/// <param name="buffer">Outgoing packet buffer</param>
/// <param name="bytesSent">Number of bytes written to the wire</param>
protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent);
// the port to listen on
internal int udpPort;
/// <summary>UDP port to bind to in server mode</summary>
protected int m_udpPort;
// the UDP socket
private Socket udpSocket;
/// <summary>Local IP address to bind to in server mode</summary>
protected IPAddress m_localBindAddress;
// the all important shutdownFlag.
private volatile bool shutdownFlag = true;
/// <summary>UDP socket, used in either client or server mode</summary>
private Socket m_udpSocket;
// the remote endpoint to communicate with
protected IPEndPoint remoteEndPoint = null;
/// <summary>Flag to process packets asynchronously or synchronously</summary>
private bool m_asyncPacketHandling;
/// <summary>The all important shutdown flag</summary>
private volatile bool m_shutdownFlag = true;
/// <summary>Returns true if the server is currently listening, otherwise false</summary>
public bool IsRunning { get { return !m_shutdownFlag; } }
/// <summary>
/// Initialize the UDP packet handler in server mode
/// Default constructor
/// </summary>
/// <param name="bindAddress">Local IP address to bind the server to</param>
/// <param name="port">Port to listening for incoming UDP packets on</param>
public OpenSimUDPBase(int port)
public OpenSimUDPBase(IPAddress bindAddress, int port)
{
udpPort = port;
m_localBindAddress = bindAddress;
m_udpPort = port;
}
/// <summary>
/// Initialize the UDP packet handler in client mode
/// Start the UDP server
/// </summary>
/// <param name="endPoint">Remote UDP server to connect to</param>
public OpenSimUDPBase(IPEndPoint endPoint)
/// <param name="recvBufferSize">The size of the receive buffer for
/// the UDP socket. This value is passed up to the operating system
/// and used in the system networking stack. Use zero to leave this
/// value as the default</param>
/// <param name="asyncPacketHandling">Set this to true to start
/// receiving more packets while current packet handler callbacks are
/// still running. Setting this to false will complete each packet
/// callback before the next packet is processed</param>
/// <remarks>This method will attempt to set the SIO_UDP_CONNRESET flag
/// on the socket to get newer versions of Windows to behave in a sane
/// manner (not throwing an exception when the remote side resets the
/// connection). This call is ignored on Mono where the flag is not
/// necessary</remarks>
public void Start(int recvBufferSize, bool asyncPacketHandling)
{
remoteEndPoint = endPoint;
udpPort = 0;
}
m_asyncPacketHandling = asyncPacketHandling;
/// <summary>
///
/// </summary>
public void Start()
{
if (shutdownFlag)
if (m_shutdownFlag)
{
const int SIO_UDP_CONNRESET = -1744830452;
IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort);
udpSocket = new Socket(
IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort);
m_udpSocket = new Socket(
AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
try
{
// this udp socket flag is not supported under mono,
// This udp socket flag is not supported under mono,
// so we'll catch the exception and continue
udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null);
m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null);
m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set");
}
catch (SocketException)
{
Logger.DebugLog("UDP SIO_UDP_CONNRESET flag not supported on this platform");
m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring");
}
udpSocket.Bind(ipep);
if (recvBufferSize != 0)
m_udpSocket.ReceiveBufferSize = recvBufferSize;
m_udpSocket.Bind(ipep);
// we're not shutting down, we're starting up
shutdownFlag = false;
m_shutdownFlag = false;
// kick off an async receive. The Start() method will return, the
// actual receives will occur asynchronously and will be caught in
@ -423,41 +141,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
/// <summary>
///
/// Stops the UDP server
/// </summary>
public void Stop()
{
if (!shutdownFlag)
if (!m_shutdownFlag)
{
// wait indefinitely for a writer lock. Once this is called, the .NET runtime
// will deny any more reader locks, in effect blocking all other send/receive
// threads. Once we have the lock, we set shutdownFlag to inform the other
// threads that the socket is closed.
shutdownFlag = true;
udpSocket.Close();
m_shutdownFlag = true;
m_udpSocket.Close();
}
}
/// <summary>
///
/// </summary>
public bool IsRunning
{
get { return !shutdownFlag; }
}
private void AsyncBeginReceive()
{
// allocate a packet buffer
//WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
UDPPacketBuffer buf = new UDPPacketBuffer();
if (!shutdownFlag)
if (!m_shutdownFlag)
{
try
{
// kick off an async read
udpSocket.BeginReceiveFrom(
m_udpSocket.BeginReceiveFrom(
//wrappedBuffer.Instance.Data,
buf.Data,
0,
@ -472,13 +182,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
if (e.SocketErrorCode == SocketError.ConnectionReset)
{
Logger.Log("SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + udpPort, Helpers.LogLevel.Error);
m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort);
bool salvaged = false;
while (!salvaged)
{
try
{
udpSocket.BeginReceiveFrom(
m_udpSocket.BeginReceiveFrom(
//wrappedBuffer.Instance.Data,
buf.Data,
0,
@ -494,7 +204,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
catch (ObjectDisposedException) { return; }
}
Logger.Log("Salvaged the UDP listener on port " + udpPort, Helpers.LogLevel.Info);
m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort);
}
}
catch (ObjectDisposedException) { }
@ -505,10 +215,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
// Asynchronous receive operations will complete here through the call
// to AsyncBeginReceive
if (!shutdownFlag)
if (!m_shutdownFlag)
{
// start another receive - this keeps the server going!
AsyncBeginReceive();
// Asynchronous mode will start another receive before the
// callback for this packet is even fired. Very parallel :-)
if (m_asyncPacketHandling)
AsyncBeginReceive();
// get the buffer that was created in AsyncBeginReceive
// this is the received data
@ -520,7 +232,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
// get the length of data actually read from the socket, store it with the
// buffer
buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
// call the abstract method PacketReceived(), passing the buffer that
// has just been filled from the socket read.
@ -528,17 +240,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
catch (SocketException) { }
catch (ObjectDisposedException) { }
//finally { wrappedBuffer.Dispose(); }
finally
{
//wrappedBuffer.Dispose();
// Synchronous mode waits until the packet callback completes
// before starting the receive to fetch another packet
if (!m_asyncPacketHandling)
AsyncBeginReceive();
}
}
}
public void AsyncBeginSend(UDPPacketBuffer buf)
{
if (!shutdownFlag)
if (!m_shutdownFlag)
{
try
{
udpSocket.BeginSendTo(
m_udpSocket.BeginSendTo(
buf.Data,
0,
buf.DataLength,
@ -557,7 +278,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
try
{
UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState;
int bytesSent = udpSocket.EndSendTo(result);
int bytesSent = m_udpSocket.EndSendTo(result);
PacketSent(buf, bytesSent);
}
@ -566,4 +287,3 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
}

View File

@ -26,6 +26,7 @@
*/
using System;
using OpenSim.Framework;
using Nini.Config;
namespace OpenSim.Region.ClientStack.LindenUDP
@ -45,12 +46,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public int Wind;
/// <summary>Drip rate for cloud packets</summary>
public int Cloud;
/// <summary>Drip rate for task (state and transaction) packets</summary>
/// <summary>Drip rate for task packets</summary>
public int Task;
/// <summary>Drip rate for texture packets</summary>
public int Texture;
/// <summary>Drip rate for asset packets</summary>
public int Asset;
/// <summary>Drip rate for state packets</summary>
public int State;
/// <summary>Drip rate for the parent token bucket</summary>
public int Total;
/// <summary>Maximum burst rate for resent packets</summary>
public int ResendLimit;
@ -66,6 +71,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public int TextureLimit;
/// <summary>Maximum burst rate for asset packets</summary>
public int AssetLimit;
/// <summary>Maximum burst rate for state packets</summary>
public int StateLimit;
/// <summary>Burst rate for the parent token bucket</summary>
public int TotalLimit;
/// <summary>
/// Default constructor
@ -77,23 +86,81 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
Resend = throttleConfig.GetInt("ResendDefault", 12500);
Land = throttleConfig.GetInt("LandDefault", 500);
Wind = throttleConfig.GetInt("WindDefault", 500);
Cloud = throttleConfig.GetInt("CloudDefault", 500);
Task = throttleConfig.GetInt("TaskDefault", 500);
Texture = throttleConfig.GetInt("TextureDefault", 500);
Asset = throttleConfig.GetInt("AssetDefault", 500);
Resend = throttleConfig.GetInt("resend_default", 12500);
Land = throttleConfig.GetInt("land_default", 500);
Wind = throttleConfig.GetInt("wind_default", 500);
Cloud = throttleConfig.GetInt("cloud_default", 500);
Task = throttleConfig.GetInt("task_default", 500);
Texture = throttleConfig.GetInt("texture_default", 500);
Asset = throttleConfig.GetInt("asset_default", 500);
State = throttleConfig.GetInt("state_default", 500);
ResendLimit = throttleConfig.GetInt("ResendLimit", 18750);
LandLimit = throttleConfig.GetInt("LandLimit", 29750);
WindLimit = throttleConfig.GetInt("WindLimit", 18750);
CloudLimit = throttleConfig.GetInt("CloudLimit", 18750);
TaskLimit = throttleConfig.GetInt("TaskLimit", 55750);
TextureLimit = throttleConfig.GetInt("TextureLimit", 55750);
AssetLimit = throttleConfig.GetInt("AssetLimit", 27500);
Total = throttleConfig.GetInt("client_throttle_max_bps", 0);
ResendLimit = throttleConfig.GetInt("resend_limit", 18750);
LandLimit = throttleConfig.GetInt("land_limit", 29750);
WindLimit = throttleConfig.GetInt("wind_limit", 18750);
CloudLimit = throttleConfig.GetInt("cloud_limit", 18750);
TaskLimit = throttleConfig.GetInt("task_limit", 18750);
TextureLimit = throttleConfig.GetInt("texture_limit", 55750);
AssetLimit = throttleConfig.GetInt("asset_limit", 27500);
State = throttleConfig.GetInt("state_limit", 37000);
TotalLimit = throttleConfig.GetInt("client_throttle_max_bps", 0);
}
catch (Exception) { }
}
public int GetRate(ThrottleOutPacketType type)
{
switch (type)
{
case ThrottleOutPacketType.Resend:
return Resend;
case ThrottleOutPacketType.Land:
return Land;
case ThrottleOutPacketType.Wind:
return Wind;
case ThrottleOutPacketType.Cloud:
return Cloud;
case ThrottleOutPacketType.Task:
return Task;
case ThrottleOutPacketType.Texture:
return Texture;
case ThrottleOutPacketType.Asset:
return Asset;
case ThrottleOutPacketType.State:
return State;
case ThrottleOutPacketType.Unknown:
default:
return 0;
}
}
public int GetLimit(ThrottleOutPacketType type)
{
switch (type)
{
case ThrottleOutPacketType.Resend:
return ResendLimit;
case ThrottleOutPacketType.Land:
return LandLimit;
case ThrottleOutPacketType.Wind:
return WindLimit;
case ThrottleOutPacketType.Cloud:
return CloudLimit;
case ThrottleOutPacketType.Task:
return TaskLimit;
case ThrottleOutPacketType.Texture:
return TextureLimit;
case ThrottleOutPacketType.Asset:
return AssetLimit;
case ThrottleOutPacketType.State:
return StateLimit;
case ThrottleOutPacketType.Unknown:
default:
return 0;
}
}
}
}

View File

@ -102,6 +102,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return false;
}
/// <summary>
/// Removes all elements from the collection
/// </summary>
public void Clear()
{
lock (SyncRoot)
packets.Clear();
}
/// <summary>
/// Gets the packet with the lowest sequence number
/// </summary>

View File

@ -43,6 +43,7 @@ using Mono.Addins;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
@ -54,7 +55,7 @@ using OpenSim.Services.Interfaces;
namespace Flotsam.RegionModules.AssetCache
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
public class FlotsamAssetCache : ISharedRegionModule, IImprovedAssetCache
public class FlotsamAssetCache : ISharedRegionModule, IImprovedAssetCache, IAssetService
{
private static readonly ILog m_log =
LogManager.GetLogger(
@ -102,6 +103,11 @@ namespace Flotsam.RegionModules.AssetCache
private System.Timers.Timer m_CachCleanTimer = new System.Timers.Timer();
private IAssetService m_AssetService = null;
private List<Scene> m_Scenes = new List<Scene>();
private bool m_DeepScanBeforePurge = false;
public FlotsamAssetCache()
{
m_InvalidChars.AddRange(Path.GetInvalidPathChars());
@ -121,6 +127,7 @@ namespace Flotsam.RegionModules.AssetCache
public void Initialise(IConfigSource source)
{
IConfig moduleConfig = source.Configs["Modules"];
if (moduleConfig != null)
{
@ -195,7 +202,13 @@ namespace Flotsam.RegionModules.AssetCache
m_CacheWarnAt = assetConfig.GetInt("CacheWarnAt", 30000);
m_DeepScanBeforePurge = assetConfig.GetBoolean("DeepScanBeforePurge", false);
MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache status", "fcache status", "Display cache status", HandleConsoleCommand);
MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache clear", "fcache clear [file] [memory]", "Remove all assets in the file and/or memory cache", HandleConsoleCommand);
MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache assets", "fcache assets", "Attempt a deep scan and cache of all assets in all scenes", HandleConsoleCommand);
MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache expire", "fcache expire <datetime>", "Purge cached assets older then the specified date/time", HandleConsoleCommand);
}
}
}
@ -213,16 +226,23 @@ namespace Flotsam.RegionModules.AssetCache
if (m_Enabled)
{
scene.RegisterModuleInterface<IImprovedAssetCache>(this);
m_Scenes.Add(scene);
//scene.AddCommand(this, "flotsamcache", "", "Display a list of console commands for the Flotsam Asset Cache", HandleConsoleCommand);
scene.AddCommand(this, "flotsamcache counts", "flotsamcache counts", "Display the number of cached assets", HandleConsoleCommand);
scene.AddCommand(this, "flotsamcache clearmem", "flotsamcache clearmem", "Remove all assets cached in memory", HandleConsoleCommand);
scene.AddCommand(this, "flotsamcache clearfile", "flotsamcache clearfile", "Remove all assets cached on disk", HandleConsoleCommand);
if (m_AssetService == null)
{
m_AssetService = scene.RequestModuleInterface<IAssetService>();
}
}
}
public void RemoveRegion(Scene scene)
{
if (m_Enabled)
{
scene.UnregisterModuleInterface<IImprovedAssetCache>(this);
m_Scenes.Remove(scene);
}
}
public void RegionLoaded(Scene scene)
@ -442,31 +462,47 @@ namespace Flotsam.RegionModules.AssetCache
if (m_LogLevel >= 2)
m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration.ToString());
// Purge all files last accessed prior to this point
DateTime purgeLine = DateTime.Now - m_FileExpiration;
// An optional deep scan at this point will ensure assets present in scenes,
// or referenced by objects in the scene, but not recently accessed
// are not purged.
if (m_DeepScanBeforePurge)
{
CacheScenes();
}
foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
{
CleanExpiredFiles(dir);
CleanExpiredFiles(dir, purgeLine);
}
}
/// <summary>
/// Recurses through specified directory checking for expired asset files and deletes them. Also removes empty directories.
/// Recurses through specified directory checking for asset files last
/// accessed prior to the specified purge line and deletes them. Also
/// removes empty tier directories.
/// </summary>
/// <param name="dir"></param>
private void CleanExpiredFiles(string dir)
private void CleanExpiredFiles(string dir, DateTime purgeLine)
{
foreach (string file in Directory.GetFiles(dir))
{
if (DateTime.Now - File.GetLastAccessTime(file) > m_FileExpiration)
if (File.GetLastAccessTime(file) < purgeLine)
{
File.Delete(file);
}
}
// Recurse into lower tiers
foreach (string subdir in Directory.GetDirectories(dir))
{
CleanExpiredFiles(subdir);
CleanExpiredFiles(subdir, purgeLine);
}
// Check if a tier directory is empty, if so, delete it
int dirSize = Directory.GetFiles(dir).Length + Directory.GetDirectories(dir).Length;
if (dirSize == 0)
{
@ -478,6 +514,11 @@ namespace Flotsam.RegionModules.AssetCache
}
}
/// <summary>
/// Determines the filename for an AssetID stored in the file cache
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private string GetFileName(string id)
{
// Would it be faster to just hash the darn thing?
@ -496,14 +537,23 @@ namespace Flotsam.RegionModules.AssetCache
return Path.Combine(path, id);
}
/// <summary>
/// Writes a file to the file cache, creating any nessesary
/// tier directories along the way
/// </summary>
/// <param name="filename"></param>
/// <param name="asset"></param>
private void WriteFileCache(string filename, AssetBase asset)
{
Stream stream = null;
// Make sure the target cache directory exists
string directory = Path.GetDirectoryName(filename);
// Write file first to a temp name, so that it doesn't look
// like it's already cached while it's still writing.
string tempname = Path.Combine(directory, Path.GetRandomFileName());
try
{
if (!Directory.Exists(directory))
@ -563,6 +613,11 @@ namespace Flotsam.RegionModules.AssetCache
}
}
/// <summary>
/// Scan through the file cache, and return number of assets currently cached.
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
private int GetFileCacheCount(string dir)
{
int count = Directory.GetFiles(dir).Length;
@ -575,55 +630,180 @@ namespace Flotsam.RegionModules.AssetCache
return count;
}
/// <summary>
/// This notes the last time the Region had a deep asset scan performed on it.
/// </summary>
/// <param name="RegionID"></param>
private void StampRegionStatusFile(UUID RegionID)
{
string RegionCacheStatusFile = Path.Combine(m_CacheDirectory, "RegionStatus_" + RegionID.ToString() + ".fac");
if (File.Exists(RegionCacheStatusFile))
{
File.SetLastWriteTime(RegionCacheStatusFile, DateTime.Now);
}
else
{
File.WriteAllText(RegionCacheStatusFile, "Please do not delete this file unless you are manually clearing your Flotsam Asset Cache.");
}
}
/// <summary>
/// Iterates through all Scenes, doing a deep scan through assets
/// to cache all assets present in the scene or referenced by assets
/// in the scene
/// </summary>
/// <returns></returns>
private int CacheScenes()
{
UuidGatherer gatherer = new UuidGatherer(m_AssetService);
Dictionary<UUID, int> assets = new Dictionary<UUID, int>();
foreach (Scene s in m_Scenes)
{
StampRegionStatusFile(s.RegionInfo.RegionID);
s.ForEachSOG(delegate(SceneObjectGroup e)
{
gatherer.GatherAssetUuids(e, assets);
}
);
}
foreach (UUID assetID in assets.Keys)
{
string filename = GetFileName(assetID.ToString());
if (File.Exists(filename))
{
File.SetLastAccessTime(filename, DateTime.Now);
}
else
{
m_AssetService.Get(assetID.ToString());
}
}
return assets.Keys.Count;
}
/// <summary>
/// Deletes all cache contents
/// </summary>
private void ClearFileCache()
{
foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
{
try
{
Directory.Delete(dir, true);
}
catch (Exception e)
{
LogException(e);
}
}
foreach (string file in Directory.GetFiles(m_CacheDirectory))
{
try
{
File.Delete(file);
}
catch (Exception e)
{
LogException(e);
}
}
}
#region Console Commands
private void HandleConsoleCommand(string module, string[] cmdparams)
{
if (cmdparams.Length == 2)
if (cmdparams.Length >= 2)
{
string cmd = cmdparams[1];
switch (cmd)
{
case "count":
case "counts":
m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache : {0}", m_MemoryCache.Count);
case "status":
m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache : {0} assets", m_MemoryCache.Count);
int fileCount = GetFileCacheCount(m_CacheDirectory);
m_log.InfoFormat("[FLOTSAM ASSET CACHE] File Cache : {0}", fileCount);
m_log.InfoFormat("[FLOTSAM ASSET CACHE] File Cache : {0} assets", fileCount);
break;
case "clearmem":
m_MemoryCache.Clear();
m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache Cleared, there are now {0} items in the memory cache", m_MemoryCache.Count);
break;
case "clearfile":
foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
foreach ( string s in Directory.GetFiles(m_CacheDirectory, "*.fac" ) )
{
try
{
Directory.Delete(dir, true);
}
catch (Exception e)
{
LogException(e);
}
}
m_log.Info("[FLOTSAM ASSET CACHE] Deep Scans were performed on the following regions:");
foreach (string file in Directory.GetFiles(m_CacheDirectory))
{
try
{
File.Delete(file);
}
catch (Exception e)
{
LogException(e);
}
string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac","");
DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s);
m_log.InfoFormat("[FLOTSAM ASSET CACHE] Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss"));
}
break;
case "clear":
if (cmdparams.Length < 3)
{
m_log.Warn("[FLOTSAM ASSET CACHE] Please specify memory and/or file cache.");
break;
}
foreach (string s in cmdparams)
{
if (s.ToLower() == "memory")
{
m_MemoryCache.Clear();
m_log.Info("[FLOTSAM ASSET CACHE] Memory cache cleared.");
}
else if (s.ToLower() == "file")
{
ClearFileCache();
m_log.Info("[FLOTSAM ASSET CACHE] File cache cleared.");
}
}
break;
case "assets":
m_log.Info("[FLOTSAM ASSET CACHE] Caching all assets, in all scenes.");
Util.FireAndForget(delegate {
int assetsCached = CacheScenes();
m_log.InfoFormat("[FLOTSAM ASSET CACHE] Completed Scene Caching, {0} assets found.", assetsCached);
});
break;
case "expire":
if (cmdparams.Length >= 3)
{
m_log.InfoFormat("[FLOTSAM ASSET CACHE] Invalid parameters for Expire, please specify a valid date & time", cmd);
break;
}
string s_expirationDate = "";
DateTime expirationDate;
if (cmdparams.Length > 3)
{
s_expirationDate = string.Join(" ", cmdparams, 2, cmdparams.Length - 2);
}
else
{
s_expirationDate = cmdparams[2];
}
if (!DateTime.TryParse(s_expirationDate, out expirationDate))
{
m_log.InfoFormat("[FLOTSAM ASSET CACHE] {0} is not a valid date & time", cmd);
break;
}
CleanExpiredFiles(m_CacheDirectory, expirationDate);
break;
default:
m_log.InfoFormat("[FLOTSAM ASSET CACHE] Unknown command {0}", cmd);
break;
@ -631,13 +811,66 @@ namespace Flotsam.RegionModules.AssetCache
}
else if (cmdparams.Length == 1)
{
m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache counts - Display the number of cached assets");
m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache status - Display cache status");
m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearmem - Remove all assets cached in memory");
m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearfile - Remove all assets cached on disk");
m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache cachescenes - Attempt a deep cache of all assets in all scenes");
m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache <datetime> - Purge assets older then the specified date & time");
}
}
#endregion
#region IAssetService Members
public AssetMetadata GetMetadata(string id)
{
AssetBase asset = Get(id);
return asset.Metadata;
}
public byte[] GetData(string id)
{
AssetBase asset = Get(id);
return asset.Data;
}
public bool Get(string id, object sender, AssetRetrieved handler)
{
AssetBase asset = Get(id);
handler(id, sender, asset);
return true;
}
public string Store(AssetBase asset)
{
if ((asset.FullID == null) || (asset.FullID == UUID.Zero))
{
asset.FullID = UUID.Random();
}
Cache(asset);
return asset.ID;
}
public bool UpdateContent(string id, byte[] data)
{
AssetBase asset = Get(id);
asset.Data = data;
Cache(asset);
return true;
}
public bool Delete(string id)
{
Expire(id);
return true;
}
#endregion
}
}

View File

@ -36,6 +36,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods
{
public class GodsModule : IRegionModule, IGodsModule
{
/// <summary>Special UUID for actions that apply to all agents</summary>
private static readonly UUID ALL_AGENTS = new UUID("44e87126-e794-4ded-05b3-7c42da3d5cdb");
protected Scene m_scene;
protected IDialogModule m_dialogModule;
@ -99,8 +102,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods
/// <param name="reason">The message to send to the user after it's been turned into a field</param>
public void KickUser(UUID godID, UUID sessionID, UUID agentID, uint kickflags, byte[] reason)
{
// For some reason the client sends this seemingly hard coded UUID for kicking everyone. Dun-know.
UUID kickUserID = new UUID("44e87126e7944ded05b37c42da3d5cdb");
UUID kickUserID = ALL_AGENTS;
ScenePresence sp = m_scene.GetScenePresence(agentID);
@ -110,15 +112,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods
{
if (agentID == kickUserID)
{
m_scene.ClientManager.ForEachClient(
string reasonStr = Utils.BytesToString(reason);
m_scene.ClientManager.ForEach(
delegate(IClientAPI controller)
{
if (controller.AgentId != godID)
controller.Kick(Utils.BytesToString(reason));
controller.Kick(reasonStr);
}
);
// This is a bit crude. It seems the client will be null before it actually stops the thread
// This is a bit crude. It seems the client will be null before it actually stops the thread
// The thread will kill itself eventually :/
// Is there another way to make sure *all* clients get this 'inter region' message?
m_scene.ForEachScenePresence(
@ -128,7 +132,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods
{
// Possibly this should really be p.Close() though that method doesn't send a close
// to the client
p.ControllingClient.Close(true);
p.ControllingClient.Close();
}
}
);
@ -138,7 +142,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods
m_scene.SceneGraph.removeUserCount(!sp.IsChildAgent);
sp.ControllingClient.Kick(Utils.BytesToString(reason));
sp.ControllingClient.Close(true);
sp.ControllingClient.Close();
}
}
else

View File

@ -1267,7 +1267,7 @@ namespace OpenSim.Region.CoreModules.InterGrid
if (avToBeKilled.IsChildAgent)
{
m_mod.DeleteOGPState(avUUID);
avToBeKilled.ControllingClient.Close(true);
avToBeKilled.ControllingClient.Close();
}
}
}

View File

@ -57,7 +57,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
public string body;
public int responseCode;
public string responseBody;
public ManualResetEvent ev;
//public ManualResetEvent ev;
public bool requestDone;
public int startTime;
public string uri;
@ -456,7 +456,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
requestData.headers["x-query-string"] = queryString;
requestData.headers["x-script-url"] = url.url;
requestData.ev = new ManualResetEvent(false);
//requestData.ev = new ManualResetEvent(false);
lock (url.requests)
{
url.requests.Add(requestID, requestData);

View File

@ -31,6 +31,7 @@ using System;
using System.Collections.Generic;
using System.Reflection;
using OpenSim.Framework;
using OpenSim.Framework.Communications.Cache;
using OpenSim.Server.Base;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
@ -40,7 +41,7 @@ using OpenMetaverse;
namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
{
public class HGAssetBroker :
ISharedRegionModule, IAssetService
ISharedRegionModule, IAssetService, IHyperAssetService
{
private static readonly ILog m_log =
LogManager.GetLogger(
@ -50,6 +51,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
private IAssetService m_GridService;
private IAssetService m_HGService;
private Scene m_aScene;
private string m_LocalAssetServiceURI;
private bool m_Enabled = false;
public Type ReplaceableInterface
@ -114,6 +118,16 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
return;
}
m_LocalAssetServiceURI = assetConfig.GetString("AssetServerURI", string.Empty);
if (m_LocalAssetServiceURI == string.Empty)
{
IConfig netConfig = source.Configs["Network"];
m_LocalAssetServiceURI = netConfig.GetString("asset_server_url", string.Empty);
}
if (m_LocalAssetServiceURI != string.Empty)
m_LocalAssetServiceURI = m_LocalAssetServiceURI.Trim('/');
m_Enabled = true;
m_log.Info("[HG ASSET CONNECTOR]: HG asset broker enabled");
}
@ -132,8 +146,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
{
if (!m_Enabled)
return;
m_aScene = scene;
scene.RegisterModuleInterface<IAssetService>(this);
scene.RegisterModuleInterface<IHyperAssetService>(this);
}
public void RemoveRegion(Scene scene)
@ -344,5 +361,30 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
else
return m_GridService.Delete(id);
}
#region IHyperAssetService
public string GetUserAssetServer(UUID userID)
{
CachedUserInfo uinfo = m_aScene.CommsManager.UserProfileCacheService.GetUserDetails(userID);
if ((uinfo != null) && (uinfo.UserProfile != null))
{
if ((uinfo.UserProfile.UserAssetURI == string.Empty) || (uinfo.UserProfile.UserAssetURI == ""))
return m_LocalAssetServiceURI;
return uinfo.UserProfile.UserAssetURI.Trim('/');
}
else
{
// we don't know anyting about this user
return string.Empty;
}
}
public string GetSimAssetServer()
{
return m_LocalAssetServiceURI;
}
#endregion
}
}

View File

@ -759,6 +759,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
}
protected bool IsLocalRegion(ulong handle)
{
return m_LocalScenes.ContainsKey(handle);

View File

@ -159,6 +159,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory
/// <returns>true if the item was successfully added</returns>
public bool AddItem(InventoryItemBase item)
{
if (item == null)
return false;
if (item.Folder == UUID.Zero)
{
InventoryFolderBase f = GetFolderForType(item.Owner, (AssetType)item.AssetType);

View File

@ -386,7 +386,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory
return false;
if (IsLocalGridUser(item.Owner))
{
return m_GridService.AddItem(item);
}
else
{
UUID sessionID = GetSessionID(item.Owner);

View File

@ -343,7 +343,9 @@ namespace OpenSim.Region.CoreModules.World.Land
lock (scene.WestBorders)
{
scene.WestBorders[0].BorderLine.Z += (int) Constants.RegionSize; //auto teleport West
scene.WestBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocX - conn.RegionScene.RegionInfo.RegionLocX) * (int)Constants.RegionSize); //auto teleport West
// Trigger auto teleport to root region
scene.WestBorders[0].TriggerRegionX = conn.RegionScene.RegionInfo.RegionLocX;
@ -410,7 +412,7 @@ namespace OpenSim.Region.CoreModules.World.Land
conn.RegionScene.WestBorders[0].BorderLine.Y += (int)Constants.RegionSize;
lock (scene.SouthBorders)
{
scene.SouthBorders[0].BorderLine.Z += (int) Constants.RegionSize; //auto teleport south
scene.SouthBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocY - conn.RegionScene.RegionInfo.RegionLocY) * (int)Constants.RegionSize); //auto teleport south
scene.SouthBorders[0].TriggerRegionX = conn.RegionScene.RegionInfo.RegionLocX;
scene.SouthBorders[0].TriggerRegionY = conn.RegionScene.RegionInfo.RegionLocY;
}
@ -481,7 +483,7 @@ namespace OpenSim.Region.CoreModules.World.Land
lock (scene.SouthBorders)
{
scene.SouthBorders[0].BorderLine.Z += (int) Constants.RegionSize; //auto teleport south
scene.SouthBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocY - conn.RegionScene.RegionInfo.RegionLocY) * (int)Constants.RegionSize); //auto teleport south
scene.SouthBorders[0].TriggerRegionX = conn.RegionScene.RegionInfo.RegionLocX;
scene.SouthBorders[0].TriggerRegionY = conn.RegionScene.RegionInfo.RegionLocY;
}
@ -503,7 +505,7 @@ namespace OpenSim.Region.CoreModules.World.Land
lock (scene.WestBorders)
{
scene.WestBorders[0].BorderLine.Z += (int) Constants.RegionSize; //auto teleport West
scene.WestBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocX - conn.RegionScene.RegionInfo.RegionLocX) * (int)Constants.RegionSize); //auto teleport West
scene.WestBorders[0].TriggerRegionX = conn.RegionScene.RegionInfo.RegionLocX;
scene.WestBorders[0].TriggerRegionY = conn.RegionScene.RegionInfo.RegionLocY;
}

View File

@ -813,7 +813,7 @@ namespace OpenSim.Region.Examples.SimpleModule
{
}
public void Close(bool ShutdownCircuit)
public void Close()
{
}
@ -833,6 +833,11 @@ namespace OpenSim.Region.Examples.SimpleModule
set { m_circuitCode = value; }
}
public IPEndPoint RemoteEndPoint
{
get { return new IPEndPoint(IPAddress.Loopback, (ushort)m_circuitCode); }
}
public void SendBlueBoxMessage(UUID FromAvatarID, String FromAvatarName, String Message)
{

View File

@ -0,0 +1,37 @@
/*
* 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 OpenSimulator 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 OpenMetaverse;
namespace OpenSim.Region.Framework.Interfaces
{
public interface IHyperAssetService
{
string GetUserAssetServer(UUID userID);
string GetSimAssetServer();
}
}

View File

@ -35,6 +35,7 @@ using OpenSim.Framework;
using OpenSim.Framework.Communications.Cache;
using OpenSim.Framework.Communications.Clients;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Services.Interfaces;
//using HyperGrid.Framework;
@ -52,13 +53,13 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid
private Scene m_scene;
private IHyperlinkService m_hyper;
IHyperlinkService HyperlinkService
private IHyperAssetService m_hyper;
IHyperAssetService HyperlinkAssets
{
get
{
if (m_hyper == null)
m_hyper = m_scene.RequestModuleInterface<IHyperlinkService>();
m_hyper = m_scene.RequestModuleInterface<IHyperAssetService>();
return m_hyper;
}
}
@ -99,7 +100,7 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid
if (asset != null)
{
m_log.Debug("[HGScene]: Asset made it to asset cache. " + asset.Name + " " + assetID);
m_log.DebugFormat("[HGScene]: Copied asset {0} from {1} to local asset server. ", asset.ID, url);
return asset;
}
return null;
@ -129,6 +130,7 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid
}
m_scene.AssetService.Store(asset1);
m_log.DebugFormat("[HGScene]: Posted copy of asset {0} from local asset server to {1}", asset1.ID, url);
}
return true;
}
@ -167,34 +169,32 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid
public void Get(UUID assetID, UUID ownerID)
{
if (!HyperlinkService.IsLocalUser(ownerID))
// Get the item from the remote asset server onto the local AssetCache
// and place an entry in m_assetMap
string userAssetURL = HyperlinkAssets.GetUserAssetServer(ownerID);
if ((userAssetURL != string.Empty) && (userAssetURL != HyperlinkAssets.GetSimAssetServer()))
{
// Get the item from the remote asset server onto the local AssetCache
// and place an entry in m_assetMap
m_log.Debug("[HGScene]: Fetching object " + assetID + " from asset server " + userAssetURL);
AssetBase asset = FetchAsset(userAssetURL, assetID);
string userAssetURL = UserAssetURL(ownerID);
if (userAssetURL != null)
if (asset != null)
{
m_log.Debug("[HGScene]: Fetching object " + assetID + " to asset server " + userAssetURL);
AssetBase asset = FetchAsset(userAssetURL, assetID);
// OK, now fetch the inside.
Dictionary<UUID, int> ids = new Dictionary<UUID, int>();
HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, userAssetURL);
uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids);
foreach (UUID uuid in ids.Keys)
FetchAsset(userAssetURL, uuid);
if (asset != null)
{
m_log.Debug("[HGScene]: Successfully fetched item from remote asset server " + userAssetURL);
m_log.DebugFormat("[HGScene]: Successfully fetched asset {0} from asset server {1}", asset.ID, userAssetURL);
// OK, now fetch the inside.
Dictionary<UUID, int> ids = new Dictionary<UUID, int>();
HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, userAssetURL);
uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids);
foreach (UUID uuid in ids.Keys)
FetchAsset(userAssetURL, uuid);
}
else
m_log.Warn("[HGScene]: Could not fetch asset from remote asset server " + userAssetURL);
}
else
m_log.Warn("[HGScene]: Unable to locate foreign user's asset server");
m_log.Warn("[HGScene]: Could not fetch asset from remote asset server " + userAssetURL);
}
else
m_log.Debug("[HGScene]: user's asset server is the local region's asset server");
}
//public InventoryItemBase Get(InventoryItemBase item, UUID rootFolder, CachedUserInfo userInfo)
@ -225,44 +225,38 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid
public void Post(UUID assetID, UUID ownerID)
{
if (!HyperlinkService.IsLocalUser(ownerID))
{
// Post the item from the local AssetCache onto the remote asset server
// and place an entry in m_assetMap
string userAssetURL = UserAssetURL(ownerID);
if (userAssetURL != null)
string userAssetURL = HyperlinkAssets.GetUserAssetServer(ownerID);
if ((userAssetURL != string.Empty) && (userAssetURL != HyperlinkAssets.GetSimAssetServer()))
{
m_log.Debug("[HGScene]: Posting object " + assetID + " to asset server " + userAssetURL);
AssetBase asset = m_scene.AssetService.Get(assetID.ToString());
if (asset != null)
{
m_log.Debug("[HGScene]: Posting object " + assetID + " to asset server " + userAssetURL);
AssetBase asset = m_scene.AssetService.Get(assetID.ToString());
if (asset != null)
Dictionary<UUID, int> ids = new Dictionary<UUID, int>();
HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, string.Empty);
uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids);
foreach (UUID uuid in ids.Keys)
{
Dictionary<UUID, int> ids = new Dictionary<UUID, int>();
HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, string.Empty);
uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids);
foreach (UUID uuid in ids.Keys)
{
asset = m_scene.AssetService.Get(uuid.ToString());
if (asset != null)
m_log.DebugFormat("[HGScene]: Posting {0} {1}", asset.Type.ToString(), asset.Name);
else
m_log.DebugFormat("[HGScene]: Could not find asset {0}", uuid);
PostAsset(userAssetURL, asset);
}
if (ids.Count > 0) // maybe it succeeded...
m_log.DebugFormat("[HGScene]: Successfully posted item {0} to remote asset server {1}", assetID, userAssetURL);
asset = m_scene.AssetService.Get(uuid.ToString());
if (asset == null)
m_log.DebugFormat("[HGScene]: Could not find asset {0}", uuid);
else
m_log.WarnFormat("[HGScene]: Could not post asset {0} to remote asset server {1}", assetID, userAssetURL);
PostAsset(userAssetURL, asset);
}
else
m_log.Debug("[HGScene]: Something wrong with asset, it could not be found");
// maybe all pieces got there...
m_log.DebugFormat("[HGScene]: Successfully posted item {0} to asset server {1}", assetID, userAssetURL);
}
else
m_log.Warn("[HGScene]: Unable to locate foreign user's asset server");
m_log.DebugFormat("[HGScene]: Something wrong with asset {0}, it could not be found", assetID);
}
else
m_log.Debug("[HGScene]: user's asset server is local region's asset server");
}
#endregion

View File

@ -32,6 +32,7 @@ using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Communications;
using OpenSim.Framework.Communications.Cache;
using OpenSim.Region.Framework.Interfaces;
namespace OpenSim.Region.Framework.Scenes.Hypergrid
{
@ -41,6 +42,21 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private HGAssetMapper m_assMapper;
public HGAssetMapper AssetMapper
{
get { return m_assMapper; }
}
private IHyperAssetService m_hyper;
private IHyperAssetService HyperAssets
{
get
{
if (m_hyper == null)
m_hyper = RequestModuleInterface<IHyperAssetService>();
return m_hyper;
}
}
#endregion
@ -140,6 +156,16 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid
}
protected override void TransferInventoryAssets(InventoryItemBase item, UUID sender, UUID receiver)
{
string userAssetServer = HyperAssets.GetUserAssetServer(sender);
if ((userAssetServer != string.Empty) && (userAssetServer != HyperAssets.GetSimAssetServer()))
m_assMapper.Get(item.AssetID, sender);
userAssetServer = HyperAssets.GetUserAssetServer(receiver);
if ((userAssetServer != string.Empty) && (userAssetServer != HyperAssets.GetSimAssetServer()))
m_assMapper.Post(item.AssetID, receiver);
}
#endregion

View File

@ -408,7 +408,7 @@ namespace OpenSim.Region.Framework.Scenes
public virtual InventoryItemBase GiveInventoryItem(
UUID recipient, UUID senderId, UUID itemId, UUID recipientFolderId)
{
Console.WriteLine("Scene.Inventory.cs: GiveInventoryItem");
//Console.WriteLine("Scene.Inventory.cs: GiveInventoryItem");
InventoryItemBase item = new InventoryItemBase(itemId, senderId);
item = InventoryService.GetItem(item);
@ -472,7 +472,8 @@ namespace OpenSim.Region.Framework.Scenes
itemCopy.SalePrice = item.SalePrice;
itemCopy.SaleType = item.SaleType;
InventoryService.AddItem(itemCopy);
if (InventoryService.AddItem(itemCopy))
TransferInventoryAssets(itemCopy, senderId, recipient);
if (!Permissions.BypassPermissions())
{
@ -494,6 +495,10 @@ namespace OpenSim.Region.Framework.Scenes
}
protected virtual void TransferInventoryAssets(InventoryItemBase item, UUID sender, UUID receiver)
{
}
/// <summary>
/// Give an entire inventory folder from one user to another. The entire contents (including all descendent
/// folders) is given.

View File

@ -390,6 +390,32 @@ namespace OpenSim.Region.Framework.Scenes
EventManager.TriggerScriptReset(part.LocalId, itemID);
}
}
void ProcessViewerEffect(IClientAPI remoteClient, List<ViewerEffectEventHandlerArg> args)
{
// TODO: don't create new blocks if recycling an old packet
List<ViewerEffectPacket.EffectBlock> effectBlock = new List<ViewerEffectPacket.EffectBlock>();
for (int i = 0; i < args.Count; i++)
{
ViewerEffectPacket.EffectBlock effect = new ViewerEffectPacket.EffectBlock();
effect.AgentID = args[i].AgentID;
effect.Color = args[i].Color;
effect.Duration = args[i].Duration;
effect.ID = args[i].ID;
effect.Type = args[i].Type;
effect.TypeData = args[i].TypeData;
effectBlock.Add(effect);
}
ViewerEffectPacket.EffectBlock[] effectBlockArray = effectBlock.ToArray();
ClientManager.ForEach(
delegate(IClientAPI client)
{
if (client.AgentId != remoteClient.AgentId)
client.SendViewerEffect(effectBlockArray);
}
);
}
/// <summary>
/// Handle a fetch inventory request from the client

View File

@ -117,6 +117,8 @@ namespace OpenSim.Region.Framework.Scenes
private volatile bool m_backingup = false;
private Dictionary<UUID, ReturnInfo> m_returns = new Dictionary<UUID, ReturnInfo>();
private Dictionary<UUID, SceneObjectGroup> m_groupsWithTargets = new Dictionary<UUID, SceneObjectGroup>();
protected string m_simulatorVersion = "OpenSimulator Server";
@ -246,8 +248,7 @@ namespace OpenSim.Region.Framework.Scenes
private int m_update_physics = 1;
private int m_update_entitymovement = 1;
private int m_update_entities = 1; // Run through all objects checking for updates
private int m_update_entitiesquick = 200; // Run through objects that have scheduled updates checking for updates
private int m_update_objects = 1; // Update objects which have scheduled themselves for updates
private int m_update_presences = 1; // Update scene presence movements
private int m_update_events = 1;
private int m_update_backup = 200;
@ -867,7 +868,7 @@ namespace OpenSim.Region.Framework.Scenes
Thread.Sleep(500);
// Stop all client threads.
ForEachScenePresence(delegate(ScenePresence avatar) { avatar.ControllingClient.Close(true); });
ForEachScenePresence(delegate(ScenePresence avatar) { avatar.ControllingClient.Close(); });
// Stop updating the scene objects and agents.
//m_heartbeatTimer.Close();
@ -979,28 +980,7 @@ namespace OpenSim.Region.Framework.Scenes
maintc = Environment.TickCount;
TimeSpan SinceLastFrame = DateTime.Now - m_lastupdate;
// Aquire a lock so only one update call happens at once
//updateLock.WaitOne();
float physicsFPS = 0;
//m_log.Info("sadfadf" + m_neighbours.Count.ToString());
int agentsInScene = m_sceneGraph.GetRootAgentCount() + m_sceneGraph.GetChildAgentCount();
if (agentsInScene > 21)
{
if (m_update_entities == 1)
{
m_update_entities = 5;
StatsReporter.SetUpdateMS(6000);
}
}
else
{
if (m_update_entities == 5)
{
m_update_entities = 1;
StatsReporter.SetUpdateMS(3000);
}
}
frameMS = Environment.TickCount;
try
@ -1013,30 +993,17 @@ namespace OpenSim.Region.Framework.Scenes
m_frame = 0;
otherMS = Environment.TickCount;
// run through all entities looking for updates (slow)
if (m_frame % m_update_entities == 0)
{
/* // Adam Experimental
if (m_updateEntitiesThread == null)
{
m_updateEntitiesThread = new Thread(m_sceneGraph.UpdateEntities);
ThreadTracker.Add(m_updateEntitiesThread);
}
// Check if any objects have reached their targets
CheckAtTargets();
// Update SceneObjectGroups that have scheduled themselves for updates
// Objects queue their updates onto all scene presences
if (m_frame % m_update_objects == 0)
m_sceneGraph.UpdateObjectGroups();
if (m_updateEntitiesThread.ThreadState == ThreadState.Stopped)
m_updateEntitiesThread.Start();
*/
m_sceneGraph.UpdateEntities();
}
// run through entities that have scheduled themselves for
// updates looking for updates(faster)
if (m_frame % m_update_entitiesquick == 0)
m_sceneGraph.ProcessUpdates();
// Run through scenepresences looking for updates
// Run through all ScenePresences looking for updates
// Presence updates and queued object updates for each presence are sent to clients
if (m_frame % m_update_presences == 0)
m_sceneGraph.UpdatePresences();
@ -1140,6 +1107,31 @@ namespace OpenSim.Region.Framework.Scenes
}
}
public void AddGroupTarget(SceneObjectGroup grp)
{
lock(m_groupsWithTargets)
m_groupsWithTargets[grp.UUID] = grp;
}
public void RemoveGroupTarget(SceneObjectGroup grp)
{
lock(m_groupsWithTargets)
m_groupsWithTargets.Remove(grp.UUID);
}
private void CheckAtTargets()
{
lock (m_groupsWithTargets)
{
foreach (KeyValuePair<UUID, SceneObjectGroup> kvp in m_groupsWithTargets)
{
kvp.Value.checkAtTargets();
}
}
}
/// <summary>
/// Send out simstats data to all clients
/// </summary>
@ -1186,10 +1178,10 @@ namespace OpenSim.Region.Framework.Scenes
if (!m_backingup)
{
m_backingup = true;
Thread backupthread = new Thread(Backup);
backupthread.Name = "BackupWriter";
backupthread.IsBackground = true;
backupthread.Start();
System.ComponentModel.BackgroundWorker backupWorker = new System.ComponentModel.BackgroundWorker();
backupWorker.DoWork += delegate(object sender, System.ComponentModel.DoWorkEventArgs e) { Backup(); };
backupWorker.RunWorkerAsync();
}
}
@ -1780,36 +1772,87 @@ namespace OpenSim.Region.Framework.Scenes
Vector3 pos = attemptedPosition;
int changeX = 1;
int changeY = 1;
if (TestBorderCross(attemptedPosition + WestCross, Cardinals.W))
{
if (TestBorderCross(attemptedPosition + SouthCross, Cardinals.S))
{
//Border crossedBorderx = GetCrossedBorder(attemptedPosition,Cardinals.W);
//Border crossedBordery = GetCrossedBorder(attemptedPosition, Cardinals.S);
Border crossedBorderx = GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W);
if (crossedBorderx.BorderLine.Z > 0)
{
pos.X = ((pos.X + crossedBorderx.BorderLine.Z));
changeX = (int)(crossedBorderx.BorderLine.Z /(int) Constants.RegionSize);
}
else
pos.X = ((pos.X + Constants.RegionSize));
Border crossedBordery = GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S);
//(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize)
pos.X = ((pos.X + Constants.RegionSize));
pos.Y = ((pos.Y + Constants.RegionSize));
if (crossedBordery.BorderLine.Z > 0)
{
pos.Y = ((pos.Y + crossedBordery.BorderLine.Z));
changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.Y = ((pos.Y + Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)((thisx - 1) * Constants.RegionSize),
(uint)((thisy - 1) * Constants.RegionSize));
= Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize),
(uint)((thisy - changeY) * Constants.RegionSize));
// x - 1
// y - 1
}
else if (TestBorderCross(attemptedPosition + NorthCross, Cardinals.N))
{
pos.X = ((pos.X + Constants.RegionSize));
pos.Y = ((pos.Y - Constants.RegionSize));
Border crossedBorderx = GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W);
if (crossedBorderx.BorderLine.Z > 0)
{
pos.X = ((pos.X + crossedBorderx.BorderLine.Z));
changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.X = ((pos.X + Constants.RegionSize));
Border crossedBordery = GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S);
//(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize)
if (crossedBordery.BorderLine.Z > 0)
{
pos.Y = ((pos.Y + crossedBordery.BorderLine.Z));
changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.Y = ((pos.Y + Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)((thisx - 1) * Constants.RegionSize),
(uint)((thisy + 1) * Constants.RegionSize));
= Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize),
(uint)((thisy + changeY) * Constants.RegionSize));
// x - 1
// y + 1
}
else
{
pos.X = ((pos.X + Constants.RegionSize));
Border crossedBorderx = GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W);
if (crossedBorderx.BorderLine.Z > 0)
{
pos.X = ((pos.X + crossedBorderx.BorderLine.Z));
changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.X = ((pos.X + Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint) ((thisx - 1)*Constants.RegionSize),
= Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize),
(uint) (thisy*Constants.RegionSize));
// x - 1
}
@ -1818,11 +1861,23 @@ namespace OpenSim.Region.Framework.Scenes
{
if (TestBorderCross(attemptedPosition + SouthCross, Cardinals.S))
{
pos.X = ((pos.X - Constants.RegionSize));
pos.Y = ((pos.Y + Constants.RegionSize));
Border crossedBordery = GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S);
//(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize)
if (crossedBordery.BorderLine.Z > 0)
{
pos.Y = ((pos.Y + crossedBordery.BorderLine.Z));
changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.Y = ((pos.Y + Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)((thisx + 1) * Constants.RegionSize),
(uint)((thisy - 1) * Constants.RegionSize));
= Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize),
(uint)((thisy - changeY) * Constants.RegionSize));
// x + 1
// y - 1
}
@ -1831,8 +1886,8 @@ namespace OpenSim.Region.Framework.Scenes
pos.X = ((pos.X - Constants.RegionSize));
pos.Y = ((pos.Y - Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)((thisx + 1) * Constants.RegionSize),
(uint)((thisy + 1) * Constants.RegionSize));
= Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize),
(uint)((thisy + changeY) * Constants.RegionSize));
// x + 1
// y + 1
}
@ -1840,16 +1895,26 @@ namespace OpenSim.Region.Framework.Scenes
{
pos.X = ((pos.X - Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint) ((thisx + 1)*Constants.RegionSize),
= Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize),
(uint) (thisy*Constants.RegionSize));
// x + 1
}
}
else if (TestBorderCross(attemptedPosition + SouthCross, Cardinals.S))
{
pos.Y = ((pos.Y + Constants.RegionSize));
Border crossedBordery = GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S);
//(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize)
if (crossedBordery.BorderLine.Z > 0)
{
pos.Y = ((pos.Y + crossedBordery.BorderLine.Z));
changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize);
}
else
pos.Y = ((pos.Y + Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy - 1) * Constants.RegionSize));
= Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy - changeY) * Constants.RegionSize));
// y - 1
}
else if (TestBorderCross(attemptedPosition + NorthCross, Cardinals.N))
@ -1857,7 +1922,7 @@ namespace OpenSim.Region.Framework.Scenes
pos.Y = ((pos.Y - Constants.RegionSize));
newRegionHandle
= Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy + 1) * Constants.RegionSize));
= Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy + changeY) * Constants.RegionSize));
// y + 1
}
@ -2363,6 +2428,8 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="client"></param>
public override void AddNewClient(IClientAPI client)
{
ClientManager.Add(client);
CheckHeartbeat();
SubscribeToClientEvents(client);
ScenePresence presence;
@ -2572,6 +2639,7 @@ namespace OpenSim.Region.Framework.Scenes
public virtual void SubscribeToClientNetworkEvents(IClientAPI client)
{
client.OnNetworkStatsUpdate += StatsReporter.AddPacketsStats;
client.OnViewerEffect += ProcessViewerEffect;
}
protected virtual void UnsubscribeToClientEvents(IClientAPI client)
@ -2726,11 +2794,9 @@ namespace OpenSim.Region.Framework.Scenes
public virtual void UnSubscribeToClientNetworkEvents(IClientAPI client)
{
client.OnNetworkStatsUpdate -= StatsReporter.AddPacketsStats;
client.OnViewerEffect -= ProcessViewerEffect;
}
/// <summary>
/// Teleport an avatar to their home region
/// </summary>
@ -3003,7 +3069,9 @@ namespace OpenSim.Region.Framework.Scenes
agentTransactions.RemoveAgentAssetTransactions(agentID);
}
// Remove the avatar from the scene
m_sceneGraph.RemoveScenePresence(agentID);
ClientManager.Remove(agentID);
try
{
@ -3052,16 +3120,6 @@ namespace OpenSim.Region.Framework.Scenes
}
}
/// <summary>
/// Closes all endpoints with the circuitcode provided.
/// </summary>
/// <param name="circuitcode">Circuit Code of the endpoint to close</param>
public override void CloseAllAgents(uint circuitcode)
{
// Called by ClientView to kill all circuit codes
ClientManager.CloseAllAgents(circuitcode);
}
/// <summary>
/// Inform all other ScenePresences on this Scene that someone else has changed position on the minimap.
/// </summary>
@ -3383,7 +3441,7 @@ namespace OpenSim.Region.Framework.Scenes
loggingOffUser.ControllingClient.Kick(message);
// Give them a second to receive the message!
Thread.Sleep(1000);
loggingOffUser.ControllingClient.Close(true);
loggingOffUser.ControllingClient.Close();
}
else
{
@ -3554,7 +3612,7 @@ namespace OpenSim.Region.Framework.Scenes
presence.ControllingClient.SendShutdownConnectionNotice();
}
presence.ControllingClient.Close(true);
presence.ControllingClient.Close();
return true;
}

View File

@ -196,8 +196,6 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="agentID"></param>
public abstract void RemoveClient(UUID agentID);
public abstract void CloseAllAgents(uint circuitcode);
#endregion
/// <summary>

View File

@ -77,7 +77,7 @@ namespace OpenSim.Region.Framework.Scenes
protected RegionInfo m_regInfo;
protected Scene m_parentScene;
protected Dictionary<UUID, EntityBase> m_updateList = new Dictionary<UUID, EntityBase>();
protected Dictionary<UUID, SceneObjectGroup> m_updateList = new Dictionary<UUID, SceneObjectGroup>();
protected int m_numRootAgents = 0;
protected int m_numPrim = 0;
protected int m_numChildAgents = 0;
@ -155,16 +155,6 @@ namespace OpenSim.Region.Framework.Scenes
}
}
protected internal void UpdateEntities()
{
List<EntityBase> updateEntities = GetEntities();
foreach (EntityBase entity in updateEntities)
{
entity.Update();
}
}
protected internal void UpdatePresences()
{
List<ScenePresence> updateScenePresences = GetScenePresences();
@ -365,12 +355,12 @@ namespace OpenSim.Region.Framework.Scenes
}
/// <summary>
/// Add an entity to the list of prims to process on the next update
/// Add an object to the list of prims to process on the next update
/// </summary>
/// <param name="obj">
/// A <see cref="EntityBase"/>
/// A <see cref="SceneObjectGroup"/>
/// </param>
protected internal void AddToUpdateList(EntityBase obj)
protected internal void AddToUpdateList(SceneObjectGroup obj)
{
lock (m_updateList)
{
@ -381,18 +371,18 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary>
/// Process all pending updates
/// </summary>
protected internal void ProcessUpdates()
protected internal void UpdateObjectGroups()
{
Dictionary<UUID, EntityBase> updates;
Dictionary<UUID, SceneObjectGroup> updates;
// Some updates add more updates to the updateList.
// Get the current list of updates and clear the list before iterating
lock (m_updateList)
{
updates = new Dictionary<UUID, EntityBase>(m_updateList);
updates = new Dictionary<UUID, SceneObjectGroup>(m_updateList);
m_updateList.Clear();
}
// Go through all timers
foreach (KeyValuePair<UUID, EntityBase> kvp in updates)
// Go through all updates
foreach (KeyValuePair<UUID, SceneObjectGroup> kvp in updates)
{
// Don't abort the whole update if one entity happens to give us an exception.
try

View File

@ -1241,6 +1241,7 @@ namespace OpenSim.Region.Framework.Scenes
{
lock (m_targets)
m_targets.Clear();
m_scene.RemoveGroupTarget(this);
}
ScheduleGroupForFullUpdate();
@ -1871,12 +1872,6 @@ namespace OpenSim.Region.Framework.Scenes
m_rootPart.UpdateFlag = 1;
lastPhysGroupPos = AbsolutePosition;
}
//foreach (SceneObjectPart part in m_parts.Values)
//{
//if (part.UpdateFlag == 0) part.UpdateFlag = 1;
//}
checkAtTargets();
if (UsePhysics && ((Math.Abs(lastPhysGroupRot.W - GroupRotation.W) > 0.1)
|| (Math.Abs(lastPhysGroupRot.X - GroupRotation.X) > 0.1)
@ -3126,6 +3121,7 @@ namespace OpenSim.Region.Framework.Scenes
{
m_targets.Add(handle, waypoint);
}
m_scene.AddGroupTarget(this);
return (int)handle;
}
@ -3133,12 +3129,13 @@ namespace OpenSim.Region.Framework.Scenes
{
lock (m_targets)
{
if (m_targets.ContainsKey((uint)handle))
m_targets.Remove((uint)handle);
m_targets.Remove((uint)handle);
if (m_targets.Count == 0)
m_scene.RemoveGroupTarget(this);
}
}
private void checkAtTargets()
public void checkAtTargets()
{
if (m_scriptListens_atTarget || m_scriptListens_notAtTarget)
{

View File

@ -61,11 +61,6 @@ namespace OpenSim.Region.Framework.Scenes.Tests
throw new NotImplementedException();
}
public override void CloseAllAgents(uint circuitcode)
{
throw new NotImplementedException();
}
public override void OtherRegionUp(GridRegion otherRegion)
{
throw new NotImplementedException();

View File

@ -64,7 +64,6 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView
void user_OnIRCReady(IRCClientView cv)
{
m_log.Info("[IRCd] Adding user...");
m_scene.ClientManager.Add(cv.CircuitCode, cv);
cv.Start();
m_log.Info("[IRCd] Added user to Scene");
}

View File

@ -634,6 +634,12 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
{
get { return (uint)Util.RandomClass.Next(0,int.MaxValue); }
}
public IPEndPoint RemoteEndPoint
{
get { return (IPEndPoint)m_client.Client.RemoteEndPoint; }
}
#pragma warning disable 67
public event GenericMessage OnGenericMessage;
public event ImprovedInstantMessage OnInstantMessage;
@ -843,7 +849,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
}
public void Close(bool ShutdownCircuit)
public void Close()
{
Disconnect();
}

View File

@ -183,8 +183,9 @@ namespace OpenSim.Region.OptionalModules.ContentManagement
public virtual void HideFromAll()
{
foreach (SceneObjectPart part in m_Entity.Children.Values)
m_Entity.Scene.ClientManager.ForEachClient(delegate(IClientAPI controller)
{ controller.SendKillObject(m_Entity.RegionHandle, part.LocalId); }
m_Entity.Scene.ClientManager.ForEach(
delegate(IClientAPI controller)
{ controller.SendKillObject(m_Entity.RegionHandle, part.LocalId); }
);
}
@ -201,8 +202,9 @@ namespace OpenSim.Region.OptionalModules.ContentManagement
public void SendFullUpdateToAll()
{
m_Entity.Scene.ClientManager.ForEachClient(delegate(IClientAPI controller)
{ m_Entity.SendFullUpdateToClient(controller); }
m_Entity.Scene.ClientManager.ForEach(
delegate(IClientAPI controller)
{ m_Entity.SendFullUpdateToClient(controller); }
);
}

View File

@ -825,7 +825,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
{
}
public void Close(bool ShutdownCircuit)
public void Close()
{
}
@ -838,11 +838,21 @@ namespace OpenSim.Region.OptionalModules.World.NPC
}
private uint m_circuitCode;
private IPEndPoint m_remoteEndPoint;
public uint CircuitCode
{
get { return m_circuitCode; }
set { m_circuitCode = value; }
set
{
m_circuitCode = value;
m_remoteEndPoint = new IPEndPoint(IPAddress.Loopback, (ushort)m_circuitCode);
}
}
public IPEndPoint RemoteEndPoint
{
get { return m_remoteEndPoint; }
}
public void SendBlueBoxMessage(UUID FromAvatarID, String FromAvatarName, String Message)

View File

@ -155,7 +155,6 @@ namespace OpenSim.Region.OptionalModules.World.NPC
NPCAvatar npcAvatar = new NPCAvatar(p_firstname, p_lastname, p_position, p_scene);
npcAvatar.CircuitCode = (uint) Util.RandomClass.Next(0, int.MaxValue);
p_scene.ClientManager.Add(npcAvatar.CircuitCode, npcAvatar);
p_scene.AddNewClient(npcAvatar);
ScenePresence sp;

View File

@ -69,7 +69,7 @@ namespace PrimMesher
public Quat Identity()
{
return new Quat(0.0f, 0.0f, 0.0f, 1.1f);
return new Quat(0.0f, 0.0f, 0.0f, 1.0f);
}
public float Length()

View File

@ -863,7 +863,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
World.Entities.TryGetValue(objecUUID, out SensedObject);
if (SensedObject == null)
{
IGroupsModule groups = World.RequestModuleInterface<IGroupsModule>();
if (groups != null)
{
GroupRecord gr = groups.GetGroupRecord(objecUUID);
if (gr != null)
return gr.GroupName;
}
return String.Empty;
}
return SensedObject.Name;
}

View File

@ -64,12 +64,17 @@ namespace OpenSim.Services.AssetService
string loaderArgs = assetConfig.GetString("AssetLoaderArgs",
String.Empty);
m_log.InfoFormat("[ASSET]: Loading default asset set from {0}", loaderArgs);
m_AssetLoader.ForEachDefaultXmlAsset(loaderArgs,
delegate(AssetBase a)
{
Store(a);
});
bool assetLoaderEnabled = assetConfig.GetBoolean("AssetLoaderEnabled", true);
if (assetLoaderEnabled)
{
m_log.InfoFormat("[ASSET]: Loading default asset set from {0}", loaderArgs);
m_AssetLoader.ForEachDefaultXmlAsset(loaderArgs,
delegate(AssetBase a)
{
Store(a);
});
}
m_log.Info("[ASSET CONNECTOR]: Local asset service enabled");
}

View File

@ -298,7 +298,7 @@ namespace OpenSim.Services.Connectors
return;
}
AssetBase asset = asset = m_Cache.Get(assetID.ToString());
AssetBase asset = m_Cache.Get(assetID.ToString());
if (asset == null)
{

View File

@ -299,6 +299,7 @@ namespace OpenSim.Services.Connectors.Inventory
if (StringToUrlAndUserID(id, out url, out userID))
{
//m_log.DebugFormat("[HGInventory CONNECTOR]: calling {0}", url);
ISessionAuthInventoryService connector = GetConnector(url);
return connector.QueryItem(userID, item, sessionID);
}

View File

@ -45,7 +45,7 @@ namespace OpenSim.Services.Connectors
LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
private string m_ServerURI = String.Empty;
// private string m_ServerURI = String.Empty;
public UserServicesConnector()
{
@ -53,7 +53,7 @@ namespace OpenSim.Services.Connectors
public UserServicesConnector(string serverURI)
{
m_ServerURI = serverURI.TrimEnd('/');
// m_ServerURI = serverURI.TrimEnd('/');
}
public UserServicesConnector(IConfigSource source)
@ -78,7 +78,7 @@ namespace OpenSim.Services.Connectors
m_log.Error("[USER CONNECTOR]: No Server URI named in section UserService");
throw new Exception("User connector init error");
}
m_ServerURI = serviceURI;
//m_ServerURI = serviceURI;
}
public UserAccount GetUserAccount(UUID scopeID, string firstName, string lastName)

View File

@ -200,6 +200,12 @@ namespace OpenSim.Services.Interfaces
Maturity = ConvertFrom.RegionSettings.Maturity;
RegionSecret = ConvertFrom.regionSecret;
EstateOwner = ConvertFrom.EstateSettings.EstateOwner;
if (EstateOwner == UUID.Zero)
{
EstateOwner = ConvertFrom.MasterAvatarAssignedUUID;
ConvertFrom.EstateSettings.EstateOwner = EstateOwner;
ConvertFrom.EstateSettings.Save();
}
}
public GridRegion(GridRegion ConvertFrom)

View File

@ -393,6 +393,11 @@ namespace OpenSim.Tests.Common.Mock
set { m_circuitCode = value; }
}
public IPEndPoint RemoteEndPoint
{
get { return new IPEndPoint(IPAddress.Loopback, (ushort)m_circuitCode); }
}
/// <summary>
/// Constructor
/// </summary>
@ -865,7 +870,7 @@ namespace OpenSim.Tests.Common.Mock
{
}
public void Close(bool ShutdownCircuit)
public void Close()
{
m_scene.RemoveClient(AgentId);
}

View File

@ -0,0 +1,60 @@
MICROSOFT PUBLIC LICENSE (Ms-PL)
This license governs use of the accompanying software. If you use the software,
you accept this license. If you do not accept the license, do not use the
software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution"
have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the
software.
A "contributor" is any person that distributes its contribution under this
license.
"Licensed patents" are a contributor's patent claims that read directly on its
contribution.
2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license
conditions and limitations in section 3, each contributor grants you a
non-exclusive, worldwide, royalty-free copyright license to reproduce its
contribution, prepare derivative works of its contribution, and distribute its
contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license
conditions and limitations in section 3, each contributor grants you a
non-exclusive, worldwide, royalty-free license under its licensed patents to
make, have made, use, sell, offer for sale, import, and/or otherwise dispose of
its contribution in the software or derivative works of the contribution in the
software.
3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any
contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you
claim are infringed by the software, your patent license from such contributor
to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all
copyright, patent, trademark, and attribution notices that are present in the
software.
(D) If you distribute any portion of the software in source code form, you may
do so only under this license by including a complete copy of this license with
your distribution. If you distribute any portion of the software in compiled or
object code form, you may only do so under a license that complies with this
license.
(E) The software is licensed "as-is." You bear the risk of using it. The
contributors give no express warranties, guarantees or conditions. You may have
additional consumer rights under your local laws which this license cannot
change. To the extent permitted under your local laws, the contributors exclude
the implied warranties of merchantability, fitness for a particular purpose and
non-infringement.

View File

@ -0,0 +1,28 @@
Copyright (c) 1999/2000 JJ2000 Partners.
This software module was originally developed by Raphaël Grosbois and
Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
Centre France S.A) in the course of development of the JPEG2000
standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
software module is an implementation of a part of the JPEG 2000
Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
Systems AB and Canon Research Centre France S.A (collectively JJ2000
Partners) agree not to assert against ISO/IEC and users of the JPEG
2000 Standard (Users) any of their rights under the copyright, not
including other intellectual property rights, for this software module
with respect to the usage by ISO/IEC and Users of this software module
or modifications thereof for use in hardware or software products
claiming conformance to the JPEG 2000 Standard. Those intending to use
this software module in hardware or software products are advised that
their use may infringe existing patents. The original developers of
this software module, JJ2000 Partners and ISO/IEC assume no liability
for use of this software module or modifications thereof. No license
or right to this software module is granted for non JPEG 2000 Standard
conforming products. JJ2000 Partners have full right to use this
software module for his/her own purpose, assign or donate this
software module to any third party and to inhibit third parties from
using this software module for non JPEG 2000 Standard conforming
products. This copyright notice must be included in all copies or
derivative works of this software module.

BIN
bin/BclExtras.dll Normal file

Binary file not shown.

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="nunit.framework" publicKeyToken="96d09a1eb7f44a77" culture="Neutral" />
<bindingRedirect oldVersion="2.0.6.0" newVersion="2.4.6.0" />
<bindingRedirect oldVersion="2.1.4.0" newVersion="2.4.6.0" />
<bindingRedirect oldVersion="2.2.8.0" newVersion="2.4.6.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<log4net>
<!-- A1 is set to be a ConsoleAppender -->
<appender name="A1" type="log4net.Appender.ConsoleAppender">
<!-- A1 uses PatternLayout -->
<layout type="log4net.Layout.PatternLayout">
<!-- Print the date in ISO 8601 format -->
<conversionPattern value="%date [%thread] %-5level %logger %ndc - %message%newline" />
</layout>
</appender>
<!-- Set root logger level to DEBUG and its only appender to A1 -->
<root>
<level value="DEBUG" />
<appender-ref ref="A1" />
</root>
</log4net>
</configuration>

View File

@ -341,26 +341,13 @@
[ClientStack.LindenUDP]
; This is the multiplier applied to all client throttles for outgoing UDP network data
; If it is set to 1, then we obey the throttle settings as given to us by the client. If it is set to 3, for example, then we
; multiply that setting by 3 (e.g. if the client gives us a setting of 250 kilobits per second then we
; will actually push down data at a maximum rate of 750 kilobits per second).
;
; In principle, setting a multiplier greater than 1 will allow data to be pushed down to a client much faster
; than its UI allows the setting to go. This may be okay in some situations, such as standalone OpenSim
; applications on a LAN. However, the greater the multipler, the higher the risk of packet drop, resulting
; in symptoms such as missing terrain or objects. A much better solution is to change the client UI to allow
; higher network bandwidth settings directly, though this isn't always possible.
;
; Currently this setting is 2 by default because we currently send much more texture data than is strictly
; necessary. A setting of 1 could result in slow texture transfer. This will be fixed when the transfer
; of textures at different levels of quality is improved.
;
; Pre r7113, this setting was not exposed but was effectively 8. You may want to try this if you encounter
; unexpected difficulties
client_throttle_multiplier = 2;
; Set this to true to process incoming packets asynchronously. Networking is
; already separated from packet handling with a queue, so this will only
; affect whether networking internals such as packet decoding and
; acknowledgement accounting are done synchronously or asynchronously
async_packet_handling = false
; the client socket receive buffer size determines how many
; The client socket receive buffer size determines how many
; incoming requests we can process; the default on .NET is 8192
; which is about 2 4k-sized UDP datagrams. On mono this is
; whatever the underlying operating system has as default; for
@ -374,12 +361,42 @@
; by the system's settings for the maximum client receive buffer
; size (on linux systems you can set that with "sysctl -w
; net.core.rmem_max=X")
;
; client_socket_rcvbuf_size = 8388608
;client_socket_rcvbuf_size = 8388608
; Maximum outbound bytes per second for a single scene. This can be used to
; throttle total outbound UDP traffic for a simulator. The default value is
; 0, meaning no throttling at the scene level. The example given here is
; 20 megabits
;scene_throttle_max_bps = 2621440
; Maximum bits per second to send to any single client. This will override the user's viewer preference settings.
; client_throttle_max_bps = 1500000
; Maximum bits per second to send to any single client. This will override
; the user's viewer preference settings. The default value is 0, meaning no
; aggregate throttling on clients (only per-category throttling). The
; example given here is 1.5 megabits
;client_throttle_max_bps = 196608
; Per-client bytes per second rates for the various throttle categories.
; These are default values that will be overriden by clients
;resend_default = 12500
;land_default = 500
;wind_default = 500
;cloud_default = 50
;task_default = 500
;texture_default = 500
;asset_default = 500
;state_default = 500
; Per-client maximum burst rates in bytes per second for the various
; throttle categories. These are default values that will be overriden by
; clients
;resend_limit = 18750
;land_limit = 29750
;wind_limit = 18750
;cloud_limit = 18750
;task_limit = 18750
;texture_limit = 55750
;asset_limit = 27500
;state_limit = 37000
[Chat]
; Controls whether the chat module is enabled. Default is true.
@ -1381,6 +1398,10 @@
[AssetService]
DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll"
AssetLoaderArgs = "assets/AssetSets.xml"
; Disable this to prevent the default asset set from being inserted into the
; asset store each time the region starts
AssetLoaderEnabled = true
[GridService]
;; default standalone, overridable in StandaloneCommon.ini

View File

@ -50,3 +50,10 @@
; Warning level for cache directory size
;CacheWarnAt = 30000
; Perform a deep scan of all assets within all regions, looking for all assets
; present or referenced. Mark all assets found that are already present in the
; cache, and request all assets that are found that are not already cached (this
; will cause those assets to be cached)
;
; DeepScanBeforePurge = false

View File

@ -135,6 +135,7 @@
<Reference name="System.Xml"/>
<Reference name="System.Data"/>
<Reference name="System.Drawing"/>
<Reference name="BclExtras.dll"/>
<Reference name="OpenMetaverseTypes.dll"/>
<Reference name="OpenMetaverse.dll"/>
<Reference name="OpenMetaverse.StructuredData.dll"/>
@ -1767,6 +1768,7 @@
<Reference name="OpenSim.Region.Communications.Local"/>
<Reference name="OpenSim.Region.Physics.Manager"/>
<Reference name="OpenSim.Services.Interfaces"/>
<Reference name="BclExtras.dll"/>
<Reference name="XMLRPC.dll"/>
<Reference name="Nini.dll" />
<Reference name="log4net.dll"/>
@ -3495,7 +3497,6 @@
<Reference name="OpenSim.Tests.Common"/>
<Reference name="OpenSim.Framework"/>
<Reference name="OpenSim.Framework.Serialization"/>
<Reference name="OpenSim.Framework.Serialization.External"/>
<Reference name="log4net.dll"/>
<Reference name="nunit.framework.dll" />