Merge branch 'master' into vehicles
commit
6deef7d0f3
|
@ -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
|
||||
|
|
|
@ -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...");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace OpenSim
|
|||
m_clientServers.Count.ToString(), m_clientServers.Count > 1 ? "s" : "");
|
||||
|
||||
WorldHasComeToAnEnd.WaitOne();
|
||||
WorldHasComeToAnEnd.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -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
|
@ -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) { }
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -1267,7 +1267,7 @@ namespace OpenSim.Region.CoreModules.InterGrid
|
|||
if (avToBeKilled.IsChildAgent)
|
||||
{
|
||||
m_mod.DeleteOGPState(avUUID);
|
||||
avToBeKilled.ControllingClient.Close(true);
|
||||
avToBeKilled.ControllingClient.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -759,6 +759,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
|
||||
}
|
||||
|
||||
|
||||
protected bool IsLocalRegion(ulong handle)
|
||||
{
|
||||
return m_LocalScenes.ContainsKey(handle);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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); }
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
Binary file not shown.
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
||||
|
|
Loading…
Reference in New Issue