mirror of
https://github.com/opensim/opensim.git
synced 2026-08-06 17:32:42 +08:00
Merge branch 'opensim:master' into master
This commit is contained in:
@@ -30,6 +30,7 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using Timer = System.Threading.Timer;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace OpenSim.Framework
|
||||
{
|
||||
@@ -460,6 +461,59 @@ namespace OpenSim.Framework
|
||||
return success;
|
||||
}
|
||||
|
||||
public ref TValue1 TryGetOrDefaultValue(TKey1 key, out bool existed)
|
||||
{
|
||||
bool gotLock = false;
|
||||
try
|
||||
{
|
||||
try { }
|
||||
finally
|
||||
{
|
||||
m_rwLock.ExitUpgradeableReadLock();
|
||||
gotLock = true;
|
||||
}
|
||||
|
||||
return ref CollectionsMarshal.GetValueRefOrAddDefault(m_values, key, out existed);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (gotLock)
|
||||
m_rwLock.ExitUpgradeableReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public ref TValue1 TryGetOrDefaultValue(TKey1 key, int expireMS, out bool existed)
|
||||
{
|
||||
bool gotLock = false;
|
||||
try
|
||||
{
|
||||
try { }
|
||||
finally
|
||||
{
|
||||
m_rwLock.EnterWriteLock();
|
||||
gotLock = true;
|
||||
}
|
||||
|
||||
ref TValue1 ret = ref CollectionsMarshal.GetValueRefOrAddDefault(m_values, key, out existed);
|
||||
int now;
|
||||
if (expireMS > 0)
|
||||
{
|
||||
expireMS = (expireMS > m_expire) ? expireMS : m_expire;
|
||||
now = (int)(Util.GetTimeStampMS() - m_startTS) + expireMS;
|
||||
}
|
||||
else
|
||||
now = int.MinValue;
|
||||
|
||||
m_expireControl[key] = now;
|
||||
return ref ret;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (gotLock)
|
||||
m_rwLock.EnterWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public TValue1[] Values
|
||||
{
|
||||
get
|
||||
|
||||
@@ -762,8 +762,8 @@ namespace OpenSim.Framework
|
||||
// There is a practical limit to region size.
|
||||
if (RegionSizeX > Constants.MaximumRegionSize || RegionSizeY > Constants.MaximumRegionSize)
|
||||
{
|
||||
RegionSizeX = Util.Clamp<uint>(RegionSizeX, Constants.RegionSize, Constants.MaximumRegionSize);
|
||||
RegionSizeY = Util.Clamp<uint>(RegionSizeY, Constants.RegionSize, Constants.MaximumRegionSize);
|
||||
RegionSizeX = Math.Clamp(RegionSizeX, Constants.RegionSize, Constants.MaximumRegionSize);
|
||||
RegionSizeY = Math.Clamp(RegionSizeY, Constants.RegionSize, Constants.MaximumRegionSize);
|
||||
m_log.ErrorFormat("{0} Region dimensions must be less than {1}. Clamping {2}'s size to <{3},{4}>",
|
||||
LogHeader, Constants.MaximumRegionSize, m_regionName, RegionSizeX, RegionSizeY);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Frozen;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
@@ -53,7 +54,7 @@ namespace OpenSim.Framework.Serialization.External
|
||||
/// <param name="xtr"></param>
|
||||
/// <returns>true on successful, false if there were any processing failures</returns>
|
||||
public static bool ExecuteReadProcessors<NodeType>(
|
||||
NodeType nodeToFill, Dictionary<string, Action<NodeType, XmlReader>> processors, XmlReader xtr)
|
||||
NodeType nodeToFill, FrozenDictionary<string, Action<NodeType, XmlReader>> processors, XmlReader xtr)
|
||||
{
|
||||
return ExecuteReadProcessors(
|
||||
nodeToFill,
|
||||
@@ -77,7 +78,7 @@ namespace OpenSim.Framework.Serialization.External
|
||||
/// <returns>true on successful, false if there were any processing failures</returns>
|
||||
public static bool ExecuteReadProcessors<NodeType>(
|
||||
NodeType nodeToFill,
|
||||
Dictionary<string, Action<NodeType, XmlReader>> processors,
|
||||
FrozenDictionary<string, Action<NodeType, XmlReader>> processors,
|
||||
XmlReader xtr,
|
||||
Action<NodeType, string, Exception> parseExceptionAction)
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
@@ -40,107 +41,66 @@ namespace OpenSim.Framework.Serialization.External
|
||||
/// </summary>
|
||||
public class LandDataSerializer
|
||||
{
|
||||
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
//private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private static Dictionary<string, Action<LandData, XmlReader>> m_ldProcessors
|
||||
= new Dictionary<string, Action<LandData, XmlReader>>();
|
||||
private static readonly FrozenDictionary<string, Action<LandData, XmlReader>> m_ldProcessors
|
||||
= new Dictionary<string, Action<LandData, XmlReader>>()
|
||||
{
|
||||
{ "Area", (ld, xtr) => ld.Area = Convert.ToInt32(xtr.ReadElementString("Area")) },
|
||||
{ "AuctionID", (ld, xtr) => ld.AuctionID = Convert.ToUInt32(xtr.ReadElementString("AuctionID")) },
|
||||
{ "AuthBuyerID", (ld, xtr) => ld.AuthBuyerID = UUID.Parse(xtr.ReadElementString("AuthBuyerID"))},
|
||||
{ "Category", (ld, xtr) => ld.Category = (ParcelCategory)Convert.ToSByte(xtr.ReadElementString("Category")) },
|
||||
{ "ClaimDate", (ld, xtr) => ld.ClaimDate = Convert.ToInt32(xtr.ReadElementString("ClaimDate")) },
|
||||
{ "ClaimPrice", (ld, xtr) => ld.ClaimPrice = Convert.ToInt32(xtr.ReadElementString("ClaimPrice")) },
|
||||
{ "GlobalID", (ld, xtr) => ld.GlobalID = UUID.Parse(xtr.ReadElementString("GlobalID")) },
|
||||
{ "GroupID", (ld, xtr) => ld.GroupID = UUID.Parse(xtr.ReadElementString("GroupID")) },
|
||||
{ "IsGroupOwned", (ld, xtr) => ld.IsGroupOwned = Convert.ToBoolean(xtr.ReadElementString("IsGroupOwned")) },
|
||||
{ "Bitmap", (ld, xtr) => ld.Bitmap = Convert.FromBase64String(xtr.ReadElementString("Bitmap")) },
|
||||
{ "Description", (ld, xtr) => ld.Description = xtr.ReadElementString("Description") },
|
||||
{ "Flags", (ld, xtr) => ld.Flags = Convert.ToUInt32(xtr.ReadElementString("Flags")) },
|
||||
{ "LandingType", (ld, xtr) => ld.LandingType = Convert.ToByte(xtr.ReadElementString("LandingType")) },
|
||||
{ "Name", (ld, xtr) => ld.Name = xtr.ReadElementString("Name") },
|
||||
{ "Status", (ld, xtr) => ld.Status = (ParcelStatus) Convert.ToSByte(xtr.ReadElementString("Status")) },
|
||||
{ "LocalID", (ld, xtr) => ld.LocalID = Convert.ToInt32(xtr.ReadElementString("LocalID")) },
|
||||
{ "MediaAutoScale", (ld, xtr) => ld.MediaAutoScale = Convert.ToByte(xtr.ReadElementString("MediaAutoScale")) },
|
||||
{ "MediaID", (ld, xtr) => ld.MediaID = UUID.Parse(xtr.ReadElementString("MediaID")) },
|
||||
{ "MediaURL", (ld, xtr) => ld.MediaURL = xtr.ReadElementString("MediaURL") },
|
||||
{ "MusicURL", (ld, xtr) => ld.MusicURL = xtr.ReadElementString("MusicURL") },
|
||||
{ "OwnerID", (ld, xtr) => ld.OwnerID = UUID.Parse(xtr.ReadElementString("OwnerID")) },
|
||||
|
||||
private static Dictionary<string, Action<LandAccessEntry, XmlReader>> m_laeProcessors
|
||||
= new Dictionary<string, Action<LandAccessEntry, XmlReader>>();
|
||||
{ "ParcelAccessList", ProcessParcelAccessList },
|
||||
|
||||
{ "Environment", ProcessParcelEnvironment },
|
||||
|
||||
{ "PassHours", (ld, xtr) => ld.PassHours = Convert.ToSingle(xtr.ReadElementString("PassHours"))},
|
||||
{ "PassPrice", (ld, xtr) => ld.PassPrice = Convert.ToInt32(xtr.ReadElementString("PassPrice"))},
|
||||
{ "SalePrice", (ld, xtr) => ld.SalePrice = Convert.ToInt32(xtr.ReadElementString("SalePrice"))},
|
||||
{ "SnapshotID", (ld, xtr) => ld.SnapshotID = UUID.Parse(xtr.ReadElementString("SnapshotID"))},
|
||||
{ "UserLocation", (ld, xtr) => ld.UserLocation = Vector3.Parse(xtr.ReadElementString("UserLocation"))},
|
||||
{ "UserLookAt", (ld, xtr) => ld.UserLookAt = Vector3.Parse(xtr.ReadElementString("UserLookAt"))},
|
||||
|
||||
{ "OtherCleanTime", (ld, xtr) => ld.OtherCleanTime = Convert.ToInt32(xtr.ReadElementString("OtherCleanTime")) },
|
||||
{ "SeeAVs", (ld, xtr) => ld.SeeAVs = xtr.ReadElementString("SeeAVs") == "1" },
|
||||
{ "AnyAVSnds", (ld, xtr) => ld.AnyAVSounds = xtr.ReadElementString("AnyAVSnds") == "1" },
|
||||
{ "GrpAVSnds", (ld, xtr) => ld.GroupAVSounds = xtr.ReadElementString("GrpAVSnds") == "1" },
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
private static readonly FrozenDictionary<string, Action<LandAccessEntry, XmlReader>> m_laeProcessors
|
||||
= new Dictionary<string, Action<LandAccessEntry, XmlReader>>()
|
||||
{
|
||||
{ "AgentID", (lae, xtr) => lae.AgentID = UUID.Parse(xtr.ReadElementString("AgentID")) },
|
||||
{ "Time", (lae, xtr) =>
|
||||
{
|
||||
// We really don't care about temp vs perm here and this
|
||||
// would break on old oars. Assume all bans are perm
|
||||
xtr.ReadElementString("Time");
|
||||
lae.Expires = 0; // Convert.ToUint( xtr.ReadElementString("Time"));
|
||||
} },
|
||||
{ "AccessList", (lae, xtr) => lae.Flags = (AccessList) Convert.ToUInt32(xtr.ReadElementString("AccessList")) }
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
static LandDataSerializer()
|
||||
{
|
||||
// LandData processors
|
||||
m_ldProcessors.Add(
|
||||
"Area", (ld, xtr) => ld.Area = Convert.ToInt32(xtr.ReadElementString("Area")));
|
||||
m_ldProcessors.Add(
|
||||
"AuctionID", (ld, xtr) => ld.AuctionID = Convert.ToUInt32(xtr.ReadElementString("AuctionID")));
|
||||
m_ldProcessors.Add(
|
||||
"AuthBuyerID", (ld, xtr) => ld.AuthBuyerID = UUID.Parse(xtr.ReadElementString("AuthBuyerID")));
|
||||
m_ldProcessors.Add(
|
||||
"Category", (ld, xtr) => ld.Category = (ParcelCategory)Convert.ToSByte(xtr.ReadElementString("Category")));
|
||||
m_ldProcessors.Add(
|
||||
"ClaimDate", (ld, xtr) => ld.ClaimDate = Convert.ToInt32(xtr.ReadElementString("ClaimDate")));
|
||||
m_ldProcessors.Add(
|
||||
"ClaimPrice", (ld, xtr) => ld.ClaimPrice = Convert.ToInt32(xtr.ReadElementString("ClaimPrice")));
|
||||
m_ldProcessors.Add(
|
||||
"GlobalID", (ld, xtr) => ld.GlobalID = UUID.Parse(xtr.ReadElementString("GlobalID")));
|
||||
m_ldProcessors.Add(
|
||||
"GroupID", (ld, xtr) => ld.GroupID = UUID.Parse(xtr.ReadElementString("GroupID")));
|
||||
m_ldProcessors.Add(
|
||||
"IsGroupOwned", (ld, xtr) => ld.IsGroupOwned = Convert.ToBoolean(xtr.ReadElementString("IsGroupOwned")));
|
||||
m_ldProcessors.Add(
|
||||
"Bitmap", (ld, xtr) => ld.Bitmap = Convert.FromBase64String(xtr.ReadElementString("Bitmap")));
|
||||
m_ldProcessors.Add(
|
||||
"Description", (ld, xtr) => ld.Description = xtr.ReadElementString("Description"));
|
||||
m_ldProcessors.Add(
|
||||
"Flags", (ld, xtr) => ld.Flags = Convert.ToUInt32(xtr.ReadElementString("Flags")));
|
||||
m_ldProcessors.Add(
|
||||
"LandingType", (ld, xtr) => ld.LandingType = Convert.ToByte(xtr.ReadElementString("LandingType")));
|
||||
m_ldProcessors.Add(
|
||||
"Name", (ld, xtr) => ld.Name = xtr.ReadElementString("Name"));
|
||||
m_ldProcessors.Add(
|
||||
"Status", (ld, xtr) => ld.Status = (ParcelStatus)Convert.ToSByte(xtr.ReadElementString("Status")));
|
||||
m_ldProcessors.Add(
|
||||
"LocalID", (ld, xtr) => ld.LocalID = Convert.ToInt32(xtr.ReadElementString("LocalID")));
|
||||
m_ldProcessors.Add(
|
||||
"MediaAutoScale", (ld, xtr) => ld.MediaAutoScale = Convert.ToByte(xtr.ReadElementString("MediaAutoScale")));
|
||||
m_ldProcessors.Add(
|
||||
"MediaID", (ld, xtr) => ld.MediaID = UUID.Parse(xtr.ReadElementString("MediaID")));
|
||||
m_ldProcessors.Add(
|
||||
"MediaURL", (ld, xtr) => ld.MediaURL = xtr.ReadElementString("MediaURL"));
|
||||
m_ldProcessors.Add(
|
||||
"MusicURL", (ld, xtr) => ld.MusicURL = xtr.ReadElementString("MusicURL"));
|
||||
m_ldProcessors.Add(
|
||||
"OwnerID", (ld, xtr) => ld.OwnerID = UUID.Parse(xtr.ReadElementString("OwnerID")));
|
||||
|
||||
m_ldProcessors.Add(
|
||||
"ParcelAccessList", ProcessParcelAccessList);
|
||||
|
||||
m_ldProcessors.Add(
|
||||
"Environment", ProcessParcelEnvironment);
|
||||
|
||||
m_ldProcessors.Add(
|
||||
"PassHours", (ld, xtr) => ld.PassHours = Convert.ToSingle(xtr.ReadElementString("PassHours")));
|
||||
m_ldProcessors.Add(
|
||||
"PassPrice", (ld, xtr) => ld.PassPrice = Convert.ToInt32(xtr.ReadElementString("PassPrice")));
|
||||
m_ldProcessors.Add(
|
||||
"SalePrice", (ld, xtr) => ld.SalePrice = Convert.ToInt32(xtr.ReadElementString("SalePrice")));
|
||||
m_ldProcessors.Add(
|
||||
"SnapshotID", (ld, xtr) => ld.SnapshotID = UUID.Parse(xtr.ReadElementString("SnapshotID")));
|
||||
m_ldProcessors.Add(
|
||||
"UserLocation", (ld, xtr) => ld.UserLocation = Vector3.Parse(xtr.ReadElementString("UserLocation")));
|
||||
m_ldProcessors.Add(
|
||||
"UserLookAt", (ld, xtr) => ld.UserLookAt = Vector3.Parse(xtr.ReadElementString("UserLookAt")));
|
||||
|
||||
// No longer used here //
|
||||
// m_ldProcessors.Add("Dwell", (landData, xtr) => return);
|
||||
|
||||
m_ldProcessors.Add(
|
||||
"OtherCleanTime", (ld, xtr) => ld.OtherCleanTime = Convert.ToInt32(xtr.ReadElementString("OtherCleanTime")));
|
||||
|
||||
m_ldProcessors.Add(
|
||||
"SeeAVs", (ld, xtr) => ld.SeeAVs = xtr.ReadElementString("SeeAVs") == "1");
|
||||
m_ldProcessors.Add(
|
||||
"AnyAVSnds", (ld, xtr) => ld.AnyAVSounds = xtr.ReadElementString("AnyAVSnds") == "1");
|
||||
m_ldProcessors.Add(
|
||||
"GrpAVSnds", (ld, xtr) => ld.GroupAVSounds = xtr.ReadElementString("GrpAVSnds") == "1");
|
||||
|
||||
// LandAccessEntryProcessors
|
||||
m_laeProcessors.Add(
|
||||
"AgentID", (lae, xtr) => lae.AgentID = UUID.Parse(xtr.ReadElementString("AgentID")));
|
||||
m_laeProcessors.Add(
|
||||
"Time", (lae, xtr) =>
|
||||
{
|
||||
// We really don't care about temp vs perm here and this
|
||||
// would break on old oars. Assume all bans are perm
|
||||
xtr.ReadElementString("Time");
|
||||
lae.Expires = 0; // Convert.ToUint( xtr.ReadElementString("Time"));
|
||||
}
|
||||
);
|
||||
m_laeProcessors.Add(
|
||||
"AccessList", (lae, xtr) => lae.Flags = (AccessList)Convert.ToUInt32(xtr.ReadElementString("AccessList")));
|
||||
|
||||
}
|
||||
|
||||
public static void ProcessParcelEnvironment(LandData ld, XmlReader xtr)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
@@ -44,34 +45,36 @@ namespace OpenSim.Framework.Serialization.External
|
||||
/// </summary>
|
||||
public class UserInventoryItemSerializer
|
||||
{
|
||||
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private static Dictionary<string, Action<InventoryItemBase, XmlReader>> m_InventoryItemXmlProcessors
|
||||
= new Dictionary<string, Action<InventoryItemBase, XmlReader>>();
|
||||
private static FrozenDictionary<string, Action<InventoryItemBase, XmlReader>> m_InventoryItemXmlProcessors
|
||||
= new Dictionary<string, Action<InventoryItemBase, XmlReader>>()
|
||||
{
|
||||
{ "Name", ProcessName },
|
||||
{ "ID", ProcessID },
|
||||
{ "InvType", ProcessInvType },
|
||||
{ "CreatorUUID", ProcessCreatorUUID },
|
||||
{ "CreatorID", ProcessCreatorID },
|
||||
{ "CreatorData", ProcessCreatorData },
|
||||
{ "CreationDate", ProcessCreationDate },
|
||||
{ "Owner", ProcessOwner },
|
||||
{ "Description", ProcessDescription },
|
||||
{ "AssetType", ProcessAssetType },
|
||||
{ "AssetID", ProcessAssetID },
|
||||
{ "SaleType", ProcessSaleType },
|
||||
{ "SalePrice", ProcessSalePrice },
|
||||
{ "BasePermissions", ProcessBasePermissions },
|
||||
{ "CurrentPermissions", ProcessCurrentPermissions },
|
||||
{ "EveryOnePermissions", ProcessEveryOnePermissions },
|
||||
{ "NextPermissions", ProcessNextPermissions },
|
||||
{ "Flags", ProcessFlags },
|
||||
{ "GroupID", ProcessGroupID },
|
||||
{ "GroupOwned", ProcessGroupOwned },
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
#region InventoryItemBase Processor initialization
|
||||
#region InventoryItemBase Processor initialization
|
||||
static UserInventoryItemSerializer()
|
||||
{
|
||||
m_InventoryItemXmlProcessors.Add("Name", ProcessName);
|
||||
m_InventoryItemXmlProcessors.Add("ID", ProcessID);
|
||||
m_InventoryItemXmlProcessors.Add("InvType", ProcessInvType);
|
||||
m_InventoryItemXmlProcessors.Add("CreatorUUID", ProcessCreatorUUID);
|
||||
m_InventoryItemXmlProcessors.Add("CreatorID", ProcessCreatorID);
|
||||
m_InventoryItemXmlProcessors.Add("CreatorData", ProcessCreatorData);
|
||||
m_InventoryItemXmlProcessors.Add("CreationDate", ProcessCreationDate);
|
||||
m_InventoryItemXmlProcessors.Add("Owner", ProcessOwner);
|
||||
m_InventoryItemXmlProcessors.Add("Description", ProcessDescription);
|
||||
m_InventoryItemXmlProcessors.Add("AssetType", ProcessAssetType);
|
||||
m_InventoryItemXmlProcessors.Add("AssetID", ProcessAssetID);
|
||||
m_InventoryItemXmlProcessors.Add("SaleType", ProcessSaleType);
|
||||
m_InventoryItemXmlProcessors.Add("SalePrice", ProcessSalePrice);
|
||||
m_InventoryItemXmlProcessors.Add("BasePermissions", ProcessBasePermissions);
|
||||
m_InventoryItemXmlProcessors.Add("CurrentPermissions", ProcessCurrentPermissions);
|
||||
m_InventoryItemXmlProcessors.Add("EveryOnePermissions", ProcessEveryOnePermissions);
|
||||
m_InventoryItemXmlProcessors.Add("NextPermissions", ProcessNextPermissions);
|
||||
m_InventoryItemXmlProcessors.Add("Flags", ProcessFlags);
|
||||
m_InventoryItemXmlProcessors.Add("GroupID", ProcessGroupID);
|
||||
m_InventoryItemXmlProcessors.Add("GroupOwned", ProcessGroupOwned);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -322,7 +322,7 @@ namespace OSHttpServer
|
||||
default:
|
||||
return;
|
||||
}
|
||||
m_processWaitEven.Set();
|
||||
m_processWaitEven?.Set();
|
||||
}
|
||||
|
||||
public static void PulseWaitSend()
|
||||
|
||||
@@ -9,6 +9,7 @@ using OSHttpServer.Parser;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using OpenMetaverse;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OSHttpServer
|
||||
{
|
||||
@@ -208,14 +209,7 @@ namespace OSHttpServer
|
||||
/// </remarks>
|
||||
public virtual void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_stream.BeginRead(m_ReceiveBuffer, 0, m_ReceiveBuffer.Length, OnReceive, null);
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
LogWriter.Write(this, LogPrio.Debug, err.ToString());
|
||||
}
|
||||
Task.Run(ReceiveLoop).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -348,63 +342,58 @@ namespace OSHttpServer
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReceive(IAsyncResult ar)
|
||||
private async void ReceiveLoop()
|
||||
{
|
||||
try
|
||||
{
|
||||
int bytesRead = 0;
|
||||
if (m_stream is null)
|
||||
return;
|
||||
try
|
||||
while (true)
|
||||
{
|
||||
bytesRead = m_stream.EndRead(ar);
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
Disconnect(SocketError.ConnectionReset);
|
||||
return;
|
||||
}
|
||||
if (m_stream == null || !m_stream.CanRead)
|
||||
return;
|
||||
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
Disconnect(SocketError.Success);
|
||||
return;
|
||||
}
|
||||
int bytesRead =
|
||||
await m_stream.ReadAsync(m_ReceiveBuffer, m_ReceiveBytesLeft, m_ReceiveBuffer.Length - m_ReceiveBytesLeft).ConfigureAwait(false);
|
||||
|
||||
if (m_isClosing)
|
||||
return;
|
||||
|
||||
m_ReceiveBytesLeft += bytesRead;
|
||||
|
||||
int offset = m_parser.Parse(m_ReceiveBuffer, 0, m_ReceiveBytesLeft);
|
||||
if (m_stream is null)
|
||||
return; // "Connection: Close" in effect.
|
||||
|
||||
if(offset > 0)
|
||||
{
|
||||
int nextBytesleft, nextOffset;
|
||||
while ((nextBytesleft = m_ReceiveBytesLeft - offset) > 0)
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
nextOffset = m_parser.Parse(m_ReceiveBuffer, offset, nextBytesleft);
|
||||
|
||||
if (m_stream is null)
|
||||
return; // "Connection: Close" in effect.
|
||||
|
||||
if (nextOffset == 0)
|
||||
break;
|
||||
|
||||
offset = nextOffset;
|
||||
Disconnect(SocketError.Success);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_isClosing)
|
||||
return;
|
||||
|
||||
m_ReceiveBytesLeft += bytesRead;
|
||||
|
||||
int offset = m_parser.Parse(m_ReceiveBuffer, 0, m_ReceiveBytesLeft);
|
||||
if (m_stream is null)
|
||||
return; // "Connection: Close" in effect.
|
||||
|
||||
if(offset > 0)
|
||||
{
|
||||
int nextBytesleft, nextOffset;
|
||||
while ((nextBytesleft = m_ReceiveBytesLeft - offset) > 0)
|
||||
{
|
||||
nextOffset = m_parser.Parse(m_ReceiveBuffer, offset, nextBytesleft);
|
||||
|
||||
if (m_stream is null)
|
||||
return; // "Connection: Close" in effect.
|
||||
|
||||
if (nextOffset == 0)
|
||||
break;
|
||||
|
||||
offset = nextOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// copy unused bytes to the beginning of the array
|
||||
if (offset > 0 && m_ReceiveBytesLeft > offset)
|
||||
Buffer.BlockCopy(m_ReceiveBuffer, offset, m_ReceiveBuffer, 0, m_ReceiveBytesLeft - offset);
|
||||
|
||||
m_ReceiveBytesLeft -= offset;
|
||||
if (StreamPassedOff)
|
||||
return; //?
|
||||
}
|
||||
|
||||
// copy unused bytes to the beginning of the array
|
||||
if (offset > 0 && m_ReceiveBytesLeft > offset)
|
||||
Buffer.BlockCopy(m_ReceiveBuffer, offset, m_ReceiveBuffer, 0, m_ReceiveBytesLeft - offset);
|
||||
|
||||
m_ReceiveBytesLeft -= offset;
|
||||
if (StreamPassedOff)
|
||||
return; //?
|
||||
m_stream.BeginRead(m_ReceiveBuffer, m_ReceiveBytesLeft, m_ReceiveBuffer.Length - m_ReceiveBytesLeft, OnReceive, null);
|
||||
}
|
||||
catch (BadRequestException err)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,8 @@ using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace OSHttpServer
|
||||
{
|
||||
@@ -19,8 +21,8 @@ namespace OSHttpServer
|
||||
|
||||
private TcpListener m_listener;
|
||||
private ILogWriter m_logWriter = NullLogWriter.Instance;
|
||||
private int m_pendingAccepts;
|
||||
private bool m_shutdown;
|
||||
public readonly CancellationTokenSource m_CancellationSource = new();
|
||||
protected RemoteCertificateValidationCallback m_clientCertValCallback = null;
|
||||
|
||||
public event EventHandler<ClientAcceptedEventArgs> Accepted;
|
||||
@@ -86,6 +88,7 @@ namespace OSHttpServer
|
||||
return new OSHttpListener(address, port, certificate, protocols);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void OnRequestReceived(object sender, RequestEventArgs e)
|
||||
{
|
||||
RequestReceived?.Invoke(sender, e);
|
||||
@@ -120,76 +123,53 @@ namespace OSHttpServer
|
||||
/// </summary>
|
||||
public bool UseTraceLogs { get; set; }
|
||||
|
||||
|
||||
/// <exception cref="Exception"><c>Exception</c>.</exception>
|
||||
private void OnAccept(IAsyncResult ar)
|
||||
private async void AcceptLoop()
|
||||
{
|
||||
bool beginAcceptCalled = false;
|
||||
try
|
||||
{
|
||||
int count = Interlocked.Decrement(ref m_pendingAccepts);
|
||||
if (m_shutdown)
|
||||
while (true)
|
||||
{
|
||||
if (count == 0)
|
||||
if (m_shutdown)
|
||||
{
|
||||
m_shutdownEvent.Set();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref m_pendingAccepts);
|
||||
m_listener.BeginAcceptSocket(OnAccept, null);
|
||||
beginAcceptCalled = true;
|
||||
Socket socket = m_listener.EndAcceptSocket(ar);
|
||||
if (!socket.Connected)
|
||||
{
|
||||
socket.Dispose();
|
||||
return;
|
||||
}
|
||||
Socket socket = await m_listener.AcceptSocketAsync(m_CancellationSource.Token).ConfigureAwait(false); ;
|
||||
if (!socket.Connected)
|
||||
{
|
||||
socket.Dispose();
|
||||
continue;
|
||||
}
|
||||
|
||||
socket.NoDelay = true;
|
||||
socket.NoDelay = true;
|
||||
|
||||
if (!OnAcceptingSocket(socket))
|
||||
{
|
||||
socket.Disconnect(true);
|
||||
return;
|
||||
}
|
||||
if (!OnAcceptingSocket(socket))
|
||||
{
|
||||
socket.Disconnect(true);
|
||||
continue;
|
||||
}
|
||||
if (socket.Connected)
|
||||
{
|
||||
m_logWriter.Write(this, LogPrio.Debug, $"Accepted connection from: {socket.RemoteEndPoint}");
|
||||
|
||||
if(socket.Connected)
|
||||
{
|
||||
m_logWriter.Write(this, LogPrio.Debug, $"Accepted connection from: {socket.RemoteEndPoint}");
|
||||
|
||||
if (m_certificate is not null)
|
||||
m_contextFactory.CreateSecureContext(socket, m_certificate, m_sslProtocols, m_clientCertValCallback);
|
||||
if (m_certificate is not null)
|
||||
m_contextFactory.CreateSecureContext(socket, m_certificate, m_sslProtocols, m_clientCertValCallback);
|
||||
else
|
||||
m_contextFactory.CreateContext(socket);
|
||||
}
|
||||
else
|
||||
m_contextFactory.CreateContext(socket);
|
||||
socket.Dispose();
|
||||
}
|
||||
else
|
||||
socket.Dispose();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
m_shutdownEvent.Set();
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
m_logWriter.Write(this, LogPrio.Debug, err.Message);
|
||||
ExceptionThrown?.Invoke(this, err);
|
||||
|
||||
if (!beginAcceptCalled)
|
||||
RetryBeginAccept();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will try to accept connections one more time.
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">If any exceptions is thrown.</exception>
|
||||
private void RetryBeginAccept()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_logWriter.Write(this, LogPrio.Error, "Trying to accept connections again.");
|
||||
m_listener.BeginAcceptSocket(OnAccept, null);
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
m_logWriter.Write(this, LogPrio.Fatal, err.Message);
|
||||
ExceptionThrown?.Invoke(this, err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,8 +201,7 @@ namespace OSHttpServer
|
||||
|
||||
m_listener = new TcpListener(m_address, m_port);
|
||||
m_listener.Start(backlog);
|
||||
Interlocked.Increment(ref m_pendingAccepts);
|
||||
m_listener.BeginAcceptSocket(OnAccept, null);
|
||||
Task.Run(AcceptLoop).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -232,10 +211,11 @@ namespace OSHttpServer
|
||||
public void Stop()
|
||||
{
|
||||
m_shutdown = true;
|
||||
m_CancellationSource.Cancel();
|
||||
m_contextFactory.Shutdown();
|
||||
m_listener.Stop();
|
||||
if (!m_shutdownEvent.WaitOne())
|
||||
m_logWriter.Write(this, LogPrio.Error, "Failed to shutdown listener properly.");
|
||||
m_listener.Stop();
|
||||
m_listener = null;
|
||||
Dispose();
|
||||
}
|
||||
@@ -251,6 +231,7 @@ namespace OSHttpServer
|
||||
if (m_shutdownEvent != null)
|
||||
{
|
||||
m_shutdownEvent.Dispose();
|
||||
m_CancellationSource.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,11 +174,15 @@ namespace OpenSim.Framework
|
||||
private static readonly string regexInvalidPathChars = $"[{new String(Path.GetInvalidPathChars())}]";
|
||||
private static readonly object XferLock = new();
|
||||
|
||||
public static readonly char[] SplitCommaArray = new char[] { ',' };
|
||||
public static readonly char[] SplitDotArray = new char[] { '.' };
|
||||
public static readonly char[] SplitColonArray = new char[] { ':' };
|
||||
public static readonly char[] SplitSemicolonArray = new char[] { ';' };
|
||||
public static readonly char[] SplitSlashArray = new char[] { '/' };
|
||||
public static readonly char[] SplitCommaArray = [','];
|
||||
public static readonly char[] SplitDotArray = ['.'];
|
||||
public static readonly char[] SplitColonArray = [':'];
|
||||
public static readonly char[] SplitSemicolonArray = [';'];
|
||||
public static readonly char[] SplitSlashArray = ['/'];
|
||||
public static readonly char[] SplitSpaceArray = [' '];
|
||||
public static readonly char[] SplitSlashSpaceArray = ['/', ' '];
|
||||
public static readonly char[] SplitDoubleQuoteSpaceArray = ['"', ' '];
|
||||
public static readonly char[] SplitSlashColonArray = ['/', ':'];
|
||||
|
||||
public static readonly XmlReaderSettings SharedXmlReaderSettings = new()
|
||||
{
|
||||
@@ -521,9 +525,9 @@ namespace OpenSim.Framework
|
||||
Uri uri = new(uristr);
|
||||
serviceURI = uri.AbsoluteUri;
|
||||
if (uri.Port == 80)
|
||||
serviceURI = $"{serviceURI.Trim(new char[] { '/', ' ' })}:80/";
|
||||
serviceURI = $"{serviceURI.Trim(SplitSlashSpaceArray)}:80/";
|
||||
else if (uri.Port == 443)
|
||||
serviceURI = $"{serviceURI.Trim(new char[] { '/', ' ' })}:443/";
|
||||
serviceURI = $"{serviceURI.Trim(SplitSlashSpaceArray)}:443/";
|
||||
serviceHost = uri.Host;
|
||||
|
||||
IPEndPoint ep = Util.getEndPoint(serviceHost, uri.Port);
|
||||
@@ -647,15 +651,16 @@ namespace OpenSim.Framework
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(regionName))
|
||||
regionName = regionName.Trim(new char[] { '"', ' ' });
|
||||
regionName = regionName.Trim(SplitDoubleQuoteSpaceArray);
|
||||
serverURI = uri.AbsoluteUri;
|
||||
if (uri.Port == 80)
|
||||
serverURI = $"{serverURI.Trim(new char[] { '/', ' ' })}:80/";
|
||||
serverURI = $"{serverURI.Trim(SplitSlashSpaceArray)}:80/";
|
||||
else if (uri.Port == 443)
|
||||
serverURI = $"{serverURI.Trim(new char[] { '/', ' ' })}:443/";
|
||||
serverURI = $"{serverURI.Trim(SplitSlashSpaceArray)}:443/";
|
||||
return true;
|
||||
}
|
||||
|
||||
//obsolete use Math.Clamp
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Clamp<T>(T x, T min, T max)
|
||||
where T : IComparable<T>
|
||||
@@ -1136,9 +1141,9 @@ namespace OpenSim.Framework
|
||||
|
||||
public static bool IsEnvironmentSupported(ref string reason)
|
||||
{
|
||||
if (Environment.Version.Major < 6)
|
||||
if (Environment.Version.Major < 8)
|
||||
{
|
||||
reason = "Dotnet 6.0 is required";
|
||||
reason = "Dotnet 8.0 is required";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1207,16 +1212,14 @@ namespace OpenSim.Framework
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static byte[] ComputeMD5Hash(string data, Encoding encoding)
|
||||
{
|
||||
using MD5 md5 = MD5.Create();
|
||||
return md5.ComputeHash(encoding.GetBytes(data));
|
||||
return MD5.HashData(encoding.GetBytes(data));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static UUID ComputeASCIIMD5UUID(string data)
|
||||
{
|
||||
using MD5 md5 = MD5.Create();
|
||||
byte[] bytes = md5.ComputeHash(Encoding.ASCII.GetBytes(data));
|
||||
UUID uuid = new(bytes,2);
|
||||
byte[] bytes = MD5.HashData(Encoding.ASCII.GetBytes(data));
|
||||
UUID uuid = new(bytes, 2);
|
||||
uuid.c &= 0x0fff;
|
||||
uuid.c |= 0x3000;
|
||||
uuid.d &= 0x3f;
|
||||
@@ -1338,8 +1341,7 @@ namespace OpenSim.Framework
|
||||
|
||||
private static byte[] ComputeSHA1Hash(byte[] src)
|
||||
{
|
||||
using SHA1 sha = SHA1.Create();
|
||||
return sha.ComputeHash(src);
|
||||
return SHA1.HashData(src);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -1357,9 +1359,7 @@ namespace OpenSim.Framework
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static UUID ComputeSHA1UUID(byte[] src)
|
||||
{
|
||||
byte[] ret;
|
||||
using (SHA1 sha = SHA1.Create())
|
||||
ret = sha.ComputeHash(src);
|
||||
byte[] ret = SHA1.HashData(src);
|
||||
UUID uuid = new(ret, 2);
|
||||
uuid.c &= 0x0fff;
|
||||
uuid.c |= 0x5000;
|
||||
@@ -1457,7 +1457,7 @@ namespace OpenSim.Framework
|
||||
{
|
||||
iv = ivString.Length == 0 ?
|
||||
Convert.FromHexString(encryptedText[..sep]):
|
||||
MD5.Create().ComputeHash(Utils.StringToBytesNoTerm(ivString));
|
||||
MD5.HashData(Utils.StringToBytesNoTerm(ivString));
|
||||
buffer = Convert.FromHexString(encryptedText[(sep + 1)..]);
|
||||
}
|
||||
catch
|
||||
@@ -1465,7 +1465,7 @@ namespace OpenSim.Framework
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
byte[] aesKey = SHA256.Create().ComputeHash(Utils.StringToBytesNoTerm(secret));
|
||||
byte[] aesKey = SHA256.HashData(Utils.StringToBytesNoTerm(secret));
|
||||
|
||||
using Aes aes = Aes.Create();
|
||||
aes.Key = aesKey;
|
||||
@@ -1683,7 +1683,21 @@ namespace OpenSim.Framework
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
private static readonly ExpiringCacheOS<string, IPAddress> dnscache = new(10000);
|
||||
private static readonly IPEndPoint dummyIPEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
private static readonly ExpiringCacheOS<string, IPAddress> dnscache = new(30000);
|
||||
private static readonly ExpiringCacheOS<SocketAddress, EndPoint> EndpointsCache = new(300000);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EndPoint GetEndPoint(SocketAddress sckaddr)
|
||||
{
|
||||
if (!EndpointsCache.TryGetValue(sckaddr, 300000, out EndPoint ep))
|
||||
{
|
||||
ep = dummyIPEndPoint.Create(sckaddr);
|
||||
EndpointsCache.AddOrUpdate(sckaddr, ep, 300);
|
||||
}
|
||||
return ep;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts a URL to a IPAddress
|
||||
@@ -1693,7 +1707,7 @@ namespace OpenSim.Framework
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IPAddress GetHostFromURL(string url)
|
||||
{
|
||||
return GetHostFromDNS(url.Split(new char[] { '/', ':' })[3]);
|
||||
return GetHostFromDNS(url.Split(SplitSlashColonArray)[3]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1752,16 +1766,14 @@ namespace OpenSim.Framework
|
||||
if (ia == null)
|
||||
return null;
|
||||
|
||||
IPEndPoint newEP;
|
||||
try
|
||||
{
|
||||
newEP = new IPEndPoint(ia, port);
|
||||
return new IPEndPoint(ia, port);
|
||||
}
|
||||
catch
|
||||
{
|
||||
newEP = null;
|
||||
return null;
|
||||
}
|
||||
return newEP;
|
||||
}
|
||||
|
||||
public static IPEndPoint getEndPoint(string hostname, int port)
|
||||
@@ -2306,7 +2318,7 @@ namespace OpenSim.Framework
|
||||
|
||||
public static void SerializeToFile(string filename, Object obj)
|
||||
{
|
||||
IFormatter formatter = new BinaryFormatter();
|
||||
var formatter = new BinaryFormatter();
|
||||
try
|
||||
{
|
||||
using Stream stream = new FileStream(filename, FileMode.Create,FileAccess.Write, FileShare.None);
|
||||
@@ -2323,7 +2335,7 @@ namespace OpenSim.Framework
|
||||
try
|
||||
{
|
||||
using Stream stream = new FileStream(filename, FileMode.Open,FileAccess.Read, FileShare.None);
|
||||
IFormatter formatter = new BinaryFormatter();
|
||||
var formatter = new BinaryFormatter();
|
||||
return formatter.Deserialize(stream);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -2560,10 +2572,10 @@ namespace OpenSim.Framework
|
||||
passPosition = connectionString.IndexOf("password", StringComparison.OrdinalIgnoreCase);
|
||||
if (passPosition == -1)
|
||||
return connectionString;
|
||||
passPosition = connectionString.IndexOf("=", passPosition);
|
||||
passPosition = connectionString.IndexOf('=', passPosition);
|
||||
if (passPosition < connectionString.Length)
|
||||
passPosition += 1;
|
||||
passEndPosition = connectionString.IndexOf(";", passPosition);
|
||||
passEndPosition = connectionString.IndexOf(';', passPosition);
|
||||
|
||||
return $"{connectionString[..passPosition]}***{connectionString[passEndPosition..]}";
|
||||
}
|
||||
@@ -3812,7 +3824,7 @@ namespace OpenSim.Framework
|
||||
};
|
||||
reader.ReadEndElement();
|
||||
|
||||
UUID.TryParse(idStr, out UUID id);
|
||||
_ = UUID.TryParse(idStr, out UUID id);
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -3949,7 +3961,7 @@ namespace OpenSim.Framework
|
||||
secret = value[indxA..];
|
||||
}
|
||||
|
||||
string[] name = n.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] name = n.Split(SplitSpaceArray, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (name.Length == 0)
|
||||
{
|
||||
firstname = string.Empty;
|
||||
@@ -4024,7 +4036,7 @@ namespace OpenSim.Framework
|
||||
secret = value[indxA..];
|
||||
}
|
||||
|
||||
string[] name = n.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] name = n.Split(SplitSpaceArray, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (name.Length == 0)
|
||||
{
|
||||
firstname = string.Empty;
|
||||
@@ -4098,7 +4110,7 @@ namespace OpenSim.Framework
|
||||
else
|
||||
n = value[indxB..seps[2]];
|
||||
|
||||
string[] name = n.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] name = n.Split(SplitSpaceArray, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (name.Length == 0)
|
||||
{
|
||||
firstname = string.Empty;
|
||||
@@ -4164,7 +4176,7 @@ namespace OpenSim.Framework
|
||||
else
|
||||
n = value[indxB..seps[2]];
|
||||
|
||||
string[] name = n.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] name = n.Split(SplitSpaceArray, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (name.Length == 0)
|
||||
{
|
||||
firstname = string.Empty;
|
||||
@@ -4422,8 +4434,8 @@ namespace OpenSim.Framework
|
||||
/// <returns>uuid[;homeURI[;first last]]</returns>
|
||||
public static string ProduceUserUniversalIdentifier(AgentCircuitData acircuit)
|
||||
{
|
||||
if (acircuit.ServiceURLs.ContainsKey("HomeURI"))
|
||||
return UniversalIdentifier(acircuit.AgentID, acircuit.firstname, acircuit.lastname, acircuit.ServiceURLs["HomeURI"].ToString());
|
||||
if (acircuit.ServiceURLs.TryGetValue("HomeURI", out object homeobj))
|
||||
return UniversalIdentifier(acircuit.AgentID, acircuit.firstname, acircuit.lastname, homeobj.ToString());
|
||||
else
|
||||
return acircuit.AgentID.ToString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user