This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
@@ -0,0 +1,292 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Web.Services.Protocols;
using Microsoft.Ccr.Core;
using Roblox.Ccr;
namespace Roblox.Common;
public class AsyncHelper
{
private struct AsyncLookupItem<T0, T1>
{
public int Index;
public T0 Key;
public PortSet<T1, Exception> Result;
public AsyncLookupItem(int index, T0 key, PortSet<T1, Exception> result)
{
Index = index;
Key = key;
Result = result;
}
}
public delegate void DoLookup<T0, T1>(T0 key, PortSet<T1, Exception> result);
private static readonly string performanceCategory;
private static readonly PerformanceCounter perfTotalAsyncCalls;
private static readonly PerformanceCounter timeoutCounts;
private static readonly PerformanceCounter riskyTimeoutCounts;
private static readonly PerformanceCounter perfPendingAsyncCallsCount;
static AsyncHelper()
{
performanceCategory = "Roblox.AsyncHelper";
if (!PerformanceCounterCategory.Exists(performanceCategory))
{
CounterCreationDataCollection collection = new CounterCreationDataCollection
{
new CounterCreationData("Pending Async Calls", string.Empty, PerformanceCounterType.NumberOfItems64),
new CounterCreationData("Total Async Calls", string.Empty, PerformanceCounterType.NumberOfItems64),
new CounterCreationData("Timeouts", string.Empty, PerformanceCounterType.NumberOfItems64),
new CounterCreationData("Risky Timeouts", string.Empty, PerformanceCounterType.NumberOfItems64)
};
PerformanceCounterCategory.Create(performanceCategory, string.Empty, PerformanceCounterCategoryType.SingleInstance, collection);
}
perfTotalAsyncCalls = new PerformanceCounter(performanceCategory, "Total Async Calls", readOnly: false);
perfTotalAsyncCalls.RawValue = 0L;
timeoutCounts = new PerformanceCounter(performanceCategory, "Timeouts", readOnly: false);
timeoutCounts.RawValue = 0L;
riskyTimeoutCounts = new PerformanceCounter(performanceCategory, "Risky Timeouts", readOnly: false);
riskyTimeoutCounts.RawValue = 0L;
perfPendingAsyncCallsCount = new PerformanceCounter(performanceCategory, "Pending Async Calls", readOnly: false);
perfPendingAsyncCallsCount.RawValue = 0L;
}
private static AsyncLookupItem<T0, T1>[] GetAsyncLookupItems<T0, T1>(ICollection<T0> lookupKeys, DoLookup<T0, T1> asyncLookup)
{
int index = 0;
AsyncLookupItem<T0, T1>[] asyncLookupItems = new AsyncLookupItem<T0, T1>[lookupKeys.Count];
foreach (T0 lookupKey in lookupKeys)
{
AsyncLookupItem<T0, T1> asyncLookupItem = new AsyncLookupItem<T0, T1>(index, lookupKey, new PortSet<T1, Exception>());
asyncLookup(asyncLookupItem.Key, asyncLookupItem.Result);
asyncLookupItems[index] = asyncLookupItem;
index++;
}
return asyncLookupItems;
}
private static IEnumerator<ITask> GetCollectionIterator<T0, T1>(ICollection<T0> keys, DoLookup<T0, T1> itemGetter, PortSet<ICollection<T1>, Exception> result)
{
AsyncLookupItem<T0, T1>[] lookupItems = GetAsyncLookupItems(keys, itemGetter);
using IEnumerator<ITask> enumerarator = HandleAsyncLookupItems(lookupItems, result);
while (enumerarator.MoveNext())
{
yield return enumerarator.Current;
}
}
private static IEnumerator<ITask> HandleAsyncLookupItems<T0, T1>(AsyncLookupItem<T0, T1>[] asyncLookupItems, PortSet<ICollection<T1>, Exception> result)
{
int countDown = asyncLookupItems.Length;
if (countDown == 0)
{
result.Post(new List<T1>());
yield break;
}
T1[] items = new T1[asyncLookupItems.Length];
for (int i = 0; i < asyncLookupItems.Length; i++)
{
AsyncLookupItem<T0, T1> asyncLookupItem = asyncLookupItems[i];
yield return (Choice)asyncLookupItem.Result;
Exception ex = asyncLookupItem.Result.Test<Exception>();
if (ex != null)
{
result.Post(ex);
break;
}
items[asyncLookupItem.Index] = asyncLookupItem.Result;
if (Interlocked.Decrement(ref countDown) == 0)
{
result.Post(items);
break;
}
}
}
public static Choice Call<TResult>(Func<AsyncCallback, object, IAsyncResult> begin, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
{
Choice choice = result;
try
{
IAsyncResult asyncResult = begin(delegate(IAsyncResult ar)
{
perfPendingAsyncCallsCount.Decrement();
PortSet<TResult, Exception> portSet2 = Interlocked.Exchange(ref result, null);
if (portSet2 != null)
{
asyncResult = null;
}
try
{
TResult item = end(ar);
portSet2?.Post(item);
}
catch (Exception item2)
{
portSet2?.Post(item2);
}
finally
{
FinalizeOnce(ref finalizer);
}
}, null);
perfPendingAsyncCallsCount.Increment();
perfTotalAsyncCalls.Increment();
if (timeout < TimeSpan.MaxValue)
{
CcrService.Singleton.Activate<Receiver<DateTime>>(Arbiter.Receive(persist: false, CcrService.Singleton.TimeoutPort(timeout), delegate(DateTime time)
{
PortSet<TResult, Exception> portSet = Interlocked.Exchange(ref result, null);
if (portSet != null)
{
portSet.Post(new TimeoutException(string.Format("AsyncHelper: timeout of {1} before {0}", end, time)));
timeoutCounts.Increment();
if (!(asyncResult is WebClientAsyncResult webClientAsyncResult))
{
riskyTimeoutCounts.Increment();
}
else
{
webClientAsyncResult.Abort();
}
}
FinalizeOnce(ref finalizer);
}));
}
}
catch (Exception ex)
{
FinalizeOnce(ref finalizer);
Interlocked.Exchange(ref result, null)?.Post(ex);
}
return choice;
}
private static void FinalizeOnce(ref Action finalizer)
{
Interlocked.Exchange(ref finalizer, null)?.Invoke();
}
public static TResult BlockingCall<TResult>(Func<AsyncCallback, object, IAsyncResult> begin, Func<IAsyncResult, TResult> end, TimeSpan timeout, Action finalizer)
{
EventWaitHandle wait = new EventWaitHandle(initialState: false, EventResetMode.ManualReset);
try
{
ExceptionPort<TResult> result = new ExceptionPort<TResult>();
Call(begin, end, result, timeout, finalizer);
CcrService.Singleton.Activate<Choice>(Arbiter.Choice(result, delegate(TResult t)
{
result.Post(t);
wait.Set();
}, delegate(Exception e)
{
result.Post(e);
wait.Set();
}));
wait.WaitOne();
return result;
}
finally
{
if (wait != null)
{
((IDisposable)wait).Dispose();
}
}
}
public static void Call<TResult, Arg0>(Func<Arg0, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
{
Call((AsyncCallback a, object o) => begin(arg0, a, o), end, result, timeout, finalizer);
}
public static Choice Call<TResult, Arg0, Arg1>(Func<Arg0, Arg1, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
{
return Call((AsyncCallback a, object o) => begin(arg0, arg1, a, o), end, result, timeout, finalizer);
}
public static void Call<TResult, Arg0, Arg1, Arg2>(Func<Arg0, Arg1, Arg2, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
{
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, a, o), end, result, timeout, finalizer);
}
public static void Call<TResult, Arg0, Arg1, Arg2, Arg3>(Func<Arg0, Arg1, Arg2, Arg3, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
{
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, arg3, a, o), end, result, timeout, finalizer);
}
public static void Call<TResult, Arg0, Arg1, Arg2, Arg3, Arg4>(Func<Arg0, Arg1, Arg2, Arg3, Arg4, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
{
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, arg3, arg4, a, o), end, result, timeout, finalizer);
}
public static void Call(Func<AsyncCallback, object, IAsyncResult> begin, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
{
Call((AsyncCallback a, object o) => begin(a, o), delegate(IAsyncResult a)
{
end(a);
return SuccessResult.Instance;
}, result, timeout, finalizer);
}
public static void Call<Arg0>(Func<Arg0, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
{
Call((AsyncCallback a, object o) => begin(arg0, a, o), delegate(IAsyncResult a)
{
end(a);
return SuccessResult.Instance;
}, result, timeout, finalizer);
}
public static void Call<Arg0, Arg1>(Func<Arg0, Arg1, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
{
Call((AsyncCallback a, object o) => begin(arg0, arg1, a, o), delegate(IAsyncResult a)
{
end(a);
return SuccessResult.Instance;
}, result, timeout, finalizer);
}
public static void Call<Arg0, Arg1, Arg2>(Func<Arg0, Arg1, Arg2, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
{
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, a, o), delegate(IAsyncResult a)
{
end(a);
return SuccessResult.Instance;
}, result, timeout, finalizer);
}
public static void Call<Arg0, Arg1, Arg2, Arg3>(Func<Arg0, Arg1, Arg2, Arg3, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
{
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, arg3, a, o), delegate(IAsyncResult a)
{
end(a);
return SuccessResult.Instance;
}, result, timeout, finalizer);
}
public static void Call<Arg0, Arg1, Arg2, Arg3, Arg4>(Func<Arg0, Arg1, Arg2, Arg3, Arg4, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
{
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, arg3, arg4, a, o), delegate(IAsyncResult a)
{
end(a);
return SuccessResult.Instance;
}, result, timeout, finalizer);
}
public static void GetCollection<T0, T1>(ICollection<T0> keys, DoLookup<T0, T1> itemGetter, PortSet<ICollection<T1>, Exception> result)
{
CcrService.Singleton.SpawnIterator(keys, itemGetter, result, GetCollectionIterator);
}
}
@@ -0,0 +1,99 @@
using System;
using Microsoft.Ccr.Core;
namespace Roblox.Common;
public class AsyncWorkQueue<T>
{
internal class WorkItem
{
private Action _CompletionTask;
private T _Item;
private SuccessFailurePort _Result;
internal Action CompletionTask => _CompletionTask;
internal T Item => _Item;
internal SuccessFailurePort Result => _Result;
internal WorkItem(T item)
{
_Item = item;
}
internal WorkItem(T item, Action completionTask)
{
_CompletionTask = completionTask;
_Item = item;
}
internal WorkItem(T item, SuccessFailurePort result)
{
_Item = item;
_Result = result;
}
}
public delegate void AsyncItemHandler(T item, SuccessFailurePort result);
private DispatcherQueue _DispatcherQueue;
private AsyncItemHandler _ItemHandler;
private Port<WorkItem> _QueuedItems = new Port<WorkItem>();
public AsyncWorkQueue(DispatcherQueue dispatcherQueue, AsyncItemHandler itemHandler)
{
if (itemHandler == null)
{
throw new ApplicationException("AsyncWorkQueue initialization failed. Valid AsyncItemHandler required.");
}
_DispatcherQueue = dispatcherQueue;
_ItemHandler = itemHandler;
Receiver<WorkItem> receiver = Arbiter.Receive(persist: true, _QueuedItems, delegate(WorkItem workItem)
{
DoWork(workItem);
});
Arbiter.Activate(_DispatcherQueue, receiver);
}
private void DoCompletionTask(SuccessFailurePort itemHandlerResult, Action completionTask)
{
Choice choice = Arbiter.Choice(itemHandlerResult, delegate
{
completionTask();
}, delegate(Exception failure)
{
ExceptionHandler.LogException(failure);
});
Arbiter.Activate(_DispatcherQueue, choice);
}
private void DoWork(WorkItem workItem)
{
SuccessFailurePort result = ((workItem.Result == null) ? new SuccessFailurePort() : workItem.Result);
_ItemHandler(workItem.Item, result);
if (workItem.CompletionTask != null)
{
DoCompletionTask(result, workItem.CompletionTask);
}
}
public void EnqueueWorkItem(T item)
{
_QueuedItems.Post(new WorkItem(item));
}
public void EnqueueWorkItem(T item, Action completionTask)
{
_QueuedItems.Post(new WorkItem(item, completionTask));
}
public void EnqueueWorkItem(T item, SuccessFailurePort result)
{
_QueuedItems.Post(new WorkItem(item, result));
}
}
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
namespace Roblox.Common;
public static class CollectionsHelper
{
public static T GetRandomElement<T>(this IList<T> self)
{
if (self.Count == 0)
{
return default(T);
}
return self[new Random().Next(self.Count)];
}
public static void Swap<T>(T[] array, int i, int j)
{
T d = array[i];
array[i] = array[j];
array[j] = d;
}
public static IEnumerable<T> RandomizeCollection<T>(this ICollection<T> collection, int numberofItemsToReturn)
{
return collection.RandomizeCollection(numberofItemsToReturn, (T _) => true);
}
public static IEnumerable<T> RandomizeCollection<T>(this ICollection<T> collection, int numberofItemsToReturn, Func<T, bool> predicate)
{
int count = collection.Count;
T[] array = new T[count];
collection.CopyTo(array, 0);
if (numberofItemsToReturn > count)
{
numberofItemsToReturn = count;
}
for (int newCount = 0; newCount < numberofItemsToReturn && newCount < count; newCount++)
{
int randIdx = new Random().Next(count);
if (!predicate(array[randIdx]))
{
count--;
Swap(array, randIdx, count);
newCount--;
}
else
{
Swap(array, newCount, randIdx);
}
}
if (numberofItemsToReturn > count)
{
numberofItemsToReturn = count;
}
for (int i = 0; i < numberofItemsToReturn; i++)
{
yield return array[i];
}
}
}
@@ -0,0 +1,6 @@
namespace Roblox.Common;
public class CompletionSignal
{
public static readonly CompletionSignal Instance = new CompletionSignal();
}
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
namespace Roblox.Common;
public static class Converters
{
public static string ConvertIntegersToCSV(int[] integers)
{
return string.Join(",", Array.ConvertAll(integers, ConvertIntegerToString));
}
private static string ConvertIntegerToString(int integer)
{
return integer.ToString();
}
public static int[] ConvertCSVToIntegers(string[] strings)
{
return Array.ConvertAll(strings, ConvertStringToInteger);
}
private static int ConvertStringToInteger(string s)
{
return int.Parse(s);
}
public static List<T> EnumToList<T>()
{
Type typeFromHandle = typeof(T);
if (typeFromHandle.BaseType != typeof(Enum))
{
throw new ArgumentException("T must be of type System.Enum");
}
Array values = Enum.GetValues(typeFromHandle);
List<T> outList = new List<T>(values.Length);
foreach (object value in values)
{
outList.Add((T)Enum.Parse(typeFromHandle, ((int)value).ToString()));
}
return outList;
}
}
@@ -0,0 +1,175 @@
using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Ccr.Core;
namespace Roblox.Common;
public sealed class CustomThreadPool : IDisposable
{
public delegate void WorkItem();
private class WaitQueueItem
{
public WaitCallback Callback;
public ExecutionContext Context;
public object State;
}
private Dispatcher _Dispatcher;
private DispatcherQueue _DispatcherQueue;
private static readonly string _PerformanceCategory = "Roblox.CustomThreadPool";
private readonly Port<WaitQueueItem> _WaitQueueItemsPort = new Port<WaitQueueItem>();
public int QueueCount
{
get
{
CheckDisposed();
return _DispatcherQueue.Count;
}
}
public int PendingTaskCount
{
get
{
CheckDisposed();
return _DispatcherQueue.Dispatcher.PendingTaskCount;
}
}
public long ProcessedTaskCount
{
get
{
CheckDisposed();
return _DispatcherQueue.Dispatcher.ProcessedTaskCount;
}
}
public int WorkerThreadCount
{
get
{
CheckDisposed();
return _DispatcherQueue.Dispatcher.WorkerThreadCount;
}
}
public CustomThreadPool(string name, int threadCount)
{
CustomThreadPool customThreadPool = this;
_Dispatcher = new Dispatcher(threadCount, ThreadPriority.Normal, DispatcherOptions.UseBackgroundThreads, $"{name} Dispatcher");
_DispatcherQueue = new PatchedDispatcherQueue($"{name} Dispatcher Queue", _Dispatcher);
Arbiter.Activate(_DispatcherQueue, Arbiter.Receive(persist: true, _WaitQueueItemsPort, ExecuteWorkItem));
Thread thread = new Thread((ThreadStart)delegate
{
customThreadPool.MonitorPerformance(name);
});
thread.IsBackground = true;
thread.Name = $"Performance Monitor: {name}";
thread.Start();
}
private void CheckDisposed()
{
if (_DispatcherQueue == null)
{
throw new ObjectDisposedException(GetType().Name);
}
}
private void ExecuteWorkItem(WaitQueueItem item)
{
try
{
ExecutionContext.Run(item.Context, item.Callback.Invoke, item.State);
}
catch (ThreadAbortException)
{
}
catch (ThreadInterruptedException)
{
}
catch (Exception ex)
{
ExceptionHandler.LogException(ex);
}
}
private void MonitorPerformance(string instanceName)
{
try
{
if (!PerformanceCounterCategory.Exists(_PerformanceCategory))
{
CounterCreationDataCollection collection = new CounterCreationDataCollection();
collection.Add(new CounterCreationData("Dispatcher Queue Count", string.Empty, PerformanceCounterType.NumberOfItems32));
collection.Add(new CounterCreationData("Dispatcher Queue Current Scheduling Rate", string.Empty, PerformanceCounterType.RateOfCountsPerSecond64));
collection.Add(new CounterCreationData("Dispatcher Queue Scheduled Task Count", string.Empty, PerformanceCounterType.NumberOfItems64));
collection.Add(new CounterCreationData("Dispatcher Pending Task Count", string.Empty, PerformanceCounterType.NumberOfItems32));
collection.Add(new CounterCreationData("Dispatcher Processed Task Count", string.Empty, PerformanceCounterType.NumberOfItems64));
collection.Add(new CounterCreationData("Dispatcher Worker Thread Count", string.Empty, PerformanceCounterType.NumberOfItems32));
PerformanceCounterCategory.Create(_PerformanceCategory, string.Empty, PerformanceCounterCategoryType.SingleInstance, collection);
}
PerformanceCounter perfDispatcherQueueCount = new PerformanceCounter(_PerformanceCategory, "Dispatcher Queue Count", readOnly: false);
PerformanceCounter perfDispatcherQueueCurrentSchedulingRate = new PerformanceCounter(_PerformanceCategory, "Dispatcher Queue Current Scheduling Rate", readOnly: false);
PerformanceCounter perfDispatcherQueueScheduledTaskCount = new PerformanceCounter(_PerformanceCategory, "Dispatcher Queue Scheduled Task Count", readOnly: false);
PerformanceCounter perfDispatcherPendingTaskCount = new PerformanceCounter(_PerformanceCategory, "Dispatcher Pending Task Count", readOnly: false);
PerformanceCounter perfDispatcherProcessedTaskCount = new PerformanceCounter(_PerformanceCategory, "Dispatcher Processed Task Count", readOnly: false);
PerformanceCounter perfDispatcherWorkerThreadCount = new PerformanceCounter(_PerformanceCategory, "Dispatcher Worker Thread Count", readOnly: false);
long num = _DispatcherQueue.ScheduledTaskCount;
while (true)
{
perfDispatcherQueueCount.RawValue = _DispatcherQueue.Count;
long scheduledTaskCount = _DispatcherQueue.ScheduledTaskCount;
perfDispatcherQueueCurrentSchedulingRate.IncrementBy(scheduledTaskCount - num);
num = (perfDispatcherQueueScheduledTaskCount.RawValue = scheduledTaskCount);
perfDispatcherPendingTaskCount.RawValue = _DispatcherQueue.Dispatcher.PendingTaskCount;
perfDispatcherProcessedTaskCount.RawValue = _DispatcherQueue.Dispatcher.ProcessedTaskCount;
perfDispatcherWorkerThreadCount.RawValue = _DispatcherQueue.Dispatcher.WorkerThreadCount;
Thread.Sleep(500);
}
}
catch (ThreadAbortException)
{
}
catch (Exception ex)
{
ExceptionHandler.LogException(ex);
}
}
public void Dispose()
{
_DispatcherQueue?.Dispose();
_DispatcherQueue = null;
_Dispatcher?.Dispose();
_Dispatcher = null;
}
public void QueueUserWorkItem(WaitCallback callback)
{
QueueUserWorkItem(callback, null);
}
public void QueueUserWorkItem(WaitCallback callback, object state)
{
CheckDisposed();
if (callback == null)
{
throw new ArgumentNullException("callback");
}
WaitQueueItem item = new WaitQueueItem();
item.Callback = callback;
item.State = state;
item.Context = ExecutionContext.Capture();
_WaitQueueItemsPort.Post(item);
}
}
@@ -0,0 +1,71 @@
using System.Diagnostics;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Text.RegularExpressions;
using Roblox.Common.Mime;
using Roblox.Common.Properties;
namespace Roblox.Common;
public class Email
{
private static string defaultSMTPServer;
private const string linebreak = "\r\n";
static Email()
{
defaultSMTPServer = Settings.Default.SMTPServer;
EventLog.WriteEntry("Web Server", $"Roblox.Common.Email.SMTPServer is {defaultSMTPServer}", EventLogEntryType.Information);
}
public static void SendEmail(string to, string from, string subject, string body)
{
SendEmail(to, from, subject, body, defaultSMTPServer);
}
public static void SendEmail(string to, string from, string subject, string body, string smtpServer)
{
if (to.Trim().Length == 0)
{
return;
}
SmtpClient smtpClient = new SmtpClient(smtpServer);
using MailMessage message = new MailMessage(from, to, subject, body);
smtpClient.Send(message);
}
public static void SendMimeEmail(string to, string from, string subject, string plaintextbody, string htmlbody)
{
ContentType mimeType = new ContentType("text/html");
AlternateView alternate = AlternateView.CreateAlternateViewFromString(htmlbody, mimeType);
SmtpClient client = new SmtpClient(defaultSMTPServer);
client.Credentials = CredentialCache.DefaultNetworkCredentials;
using MailMessage message = new MailMessage(from, to, subject, plaintextbody);
message.AlternateViews.Add(alternate);
client.Send(message);
}
public static void SendMimeEmail(string to, string from, string subject, string plaintextbody, string htmlbody, string smtpServer)
{
if (to.Trim().Length == 0)
{
return;
}
ContentType mimeType = new ContentType("text/html");
AlternateView alternate = AlternateView.CreateAlternateViewFromString(htmlbody, mimeType);
SmtpClient client = new SmtpClient(smtpServer);
client.Credentials = CredentialCache.DefaultNetworkCredentials;
using MailMessage message = new MailMessage(from, to, subject, plaintextbody);
message.AlternateViews.Add(alternate);
client.Send(message);
}
public static MailMessageEx ParseEmail(string rawEmail)
{
MimeReader reader = new MimeReader(Regex.Split(rawEmail, "\r\n"));
MimeEntity mime = reader.CreateMimeEntity();
return mime.ToMailMessageEx();
}
}
@@ -0,0 +1,16 @@
namespace Roblox.Common;
public class EntityCollectionItem
{
public static int ApiVersion = 1;
public string id { get; private set; }
public string __stamp { get; private set; }
public EntityCollectionItem(string id, string stamp)
{
this.id = id;
__stamp = stamp;
}
}
@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace Roblox.Common;
public class EntityCollectionJson : Json
{
public static int ApiVersion = 1;
public IEnumerable<EntityCollectionItem> data { get; private set; }
public EntityCollectionJson(IEnumerable<EntityCollectionItem> data)
{
this.data = data;
}
}
@@ -0,0 +1,13 @@
namespace Roblox.Common;
public class EntityCountJson : Json
{
public static int ApiVersion = 1;
public string count { get; private set; }
public EntityCountJson(string count)
{
this.count = count;
}
}
@@ -0,0 +1,140 @@
using System;
using System.Threading;
namespace Roblox.Common;
public class EventCounter : IDisposable
{
private class Bucket
{
private long eventCount;
public long EventCount => eventCount;
public Bucket(long value)
{
eventCount = value;
}
public void LogEvent()
{
Interlocked.Increment(ref eventCount);
}
}
private class BucketAggregation
{
private Bucket current;
private BucketAggregation next;
public readonly TimeSpan Span;
public BucketAggregation(TimeSpan span)
{
Span = span;
}
public void AddBucket(Bucket bucket)
{
if (current == null)
{
current = bucket;
return;
}
if (next == null)
{
next = new BucketAggregation(Span.Add(Span));
}
next.AddBucket(new Bucket(current.EventCount + bucket.EventCount));
current = null;
}
public long GetEventCount(TimeSpan span)
{
if (current != null)
{
if (span == Span)
{
return current.EventCount;
}
if (span > Span && next != null)
{
return current.EventCount + next.GetEventCount(span.Subtract(Span));
}
return current.EventCount * span.Ticks / span.Ticks;
}
if (next != null)
{
return next.GetEventCount(span);
}
return 0L;
}
public long GetTotalEventCount()
{
long eventCount = 0L;
if (current != null)
{
eventCount += current.EventCount;
}
if (next != null)
{
eventCount += next.GetTotalEventCount();
}
return eventCount;
}
}
private Bucket currentBucket;
private readonly BucketAggregation head = new BucketAggregation(SmallestInterval);
private readonly Timer timer;
public static readonly TimeSpan SmallestInterval = TimeSpan.FromSeconds(2.0);
public EventCounter()
{
currentBucket = new Bucket(0L);
timer = new Timer(delegate
{
head.AddBucket(currentBucket);
currentBucket = new Bucket(0L);
}, null, SmallestInterval, SmallestInterval);
}
public long GetEventCount(TimeSpan sample)
{
if (sample == TimeSpan.MaxValue)
{
return GetTotalEventCount();
}
return head.GetEventCount(sample);
}
public double GetEventsPerSecond(TimeSpan sample)
{
long eventCount = GetEventCount(sample);
if (sample == TimeSpan.Zero)
{
return 0.0;
}
return (double)eventCount / sample.TotalSeconds;
}
public long GetTotalEventCount()
{
return head.GetTotalEventCount();
}
public void LogEvent()
{
currentBucket.LogEvent();
}
public void Dispose()
{
timer.Dispose();
}
}
@@ -0,0 +1,578 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using Microsoft.Ccr.Core;
namespace Roblox.Common;
public static class Extensions
{
public enum QueryOperators
{
AND,
OR,
NOT,
NEAR
}
private static readonly char quote = '"';
private static readonly char space = ' ';
private static readonly char[] spaceDelimiter = new char[1] { space };
public static IEnumerable<T> AsEnumerable<T>(this T item)
{
yield return item;
}
private static IEnumerator<ITask> StreamToArrayIterator(Stream stream, PortSet<byte[], Exception> result)
{
if (stream is MemoryStream str2)
{
result.Post(str2.ToArray());
yield break;
}
byte[] buff = new byte[1024];
MemoryStream memoryStream;
MemoryStream str = (memoryStream = new MemoryStream());
using (memoryStream)
{
PortSet<int, Exception> readPort = new PortSet<int, Exception>();
Exception ex;
while (true)
{
AsyncHelper.Call(stream.BeginRead, buff, 0, buff.Length, stream.EndRead, readPort, TimeSpan.FromMinutes(1.0), null);
yield return (ITask)readPort;
ex = readPort;
if (ex != null)
{
break;
}
PortSet<int, Exception> index = readPort;
if ((int)index == 0)
{
result.Post(str.ToArray());
yield break;
}
str.Write(buff, 0, index);
}
result.Post(ex);
}
}
private static IEnumerator<ITask> StreamToArrayAndDisposeIterator(Stream stream, PortSet<byte[], Exception> result)
{
using (stream)
{
if (stream is MemoryStream memStream2)
{
result.Post(memStream2.ToArray());
yield break;
}
byte[] buff = new byte[1024];
MemoryStream memStream = new MemoryStream();
using (memStream)
{
PortSet<int, Exception> readPort = new PortSet<int, Exception>();
while (true)
{
AsyncHelper.Call(stream.BeginRead, buff, 0, buff.Length, stream.EndRead, readPort, TimeSpan.FromMinutes(1.0), null);
yield return (Choice)readPort;
Exception ex = readPort;
if (ex != null)
{
result.Post(ex);
yield break;
}
int index = readPort;
if (index == 0)
{
break;
}
memStream.Write(buff, 0, index);
}
result.Post(memStream.ToArray());
}
}
}
private static IEnumerator<ITask> StreamToStringIterator(Stream stream, PortSet<string, Exception> result)
{
ASCIIEncoding encoding = new ASCIIEncoding();
if (stream is MemoryStream memStream2)
{
result.Post(encoding.GetString(memStream2.ToArray()));
yield break;
}
byte[] buf = new byte[1024];
MemoryStream memStream = new MemoryStream();
using (memStream)
{
PortSet<int, Exception> readPort = new PortSet<int, Exception>();
while (true)
{
AsyncHelper.Call(stream.BeginRead, buf, 0, buf.Length, stream.EndRead, readPort, TimeSpan.FromMinutes(1.0), null);
yield return (Choice)readPort;
Exception ex = readPort;
if (ex != null)
{
result.Post(ex);
yield break;
}
int index = readPort;
if (index == 0)
{
break;
}
memStream.Write(buf, 0, index);
}
result.Post(encoding.GetString(memStream.ToArray()));
}
}
private static IEnumerator<ITask> StreamToStringAndDisposeIterator(Stream stream, PortSet<string, Exception> result)
{
using (stream)
{
ASCIIEncoding encoding = new ASCIIEncoding();
if (stream is MemoryStream memStream2)
{
result.Post(encoding.GetString(memStream2.ToArray()));
yield break;
}
byte[] buff = new byte[1024];
MemoryStream memStream = new MemoryStream();
using (memStream)
{
PortSet<int, Exception> readPort = new PortSet<int, Exception>();
while (true)
{
AsyncHelper.Call(stream.BeginRead, buff, 0, buff.Length, stream.EndRead, readPort, TimeSpan.FromMinutes(1.0), null);
yield return (Choice)readPort;
Exception ex = readPort;
if (ex != null)
{
result.Post(ex);
yield break;
}
int index = readPort;
if (index == 0)
{
break;
}
memStream.Write(buff, 0, index);
}
result.Post(encoding.GetString(memStream.ToArray()));
}
}
}
public static void Exists(this FileInfo fileInfo, PortSet<bool, Exception> result)
{
FileHelper.ExecuteTask(File.Exists, fileInfo.FullName, result);
}
public static void ToArray(this Stream stream, PortSet<byte[], Exception> result)
{
CcrService.Singleton.SpawnIterator(stream, result, StreamToArrayIterator);
}
public static void ToArrayAndDispose(this Stream stream, PortSet<byte[], Exception> result)
{
CcrService.Singleton.SpawnIterator(stream, result, StreamToArrayAndDisposeIterator);
}
public static void ToString(this Stream stream, PortSet<string, Exception> result)
{
CcrService.Singleton.SpawnIterator(stream, result, StreamToStringIterator);
}
public static void ToStringAndDispose(this Stream stream, PortSet<string, Exception> result)
{
CcrService.Singleton.SpawnIterator(stream, result, StreamToStringIterator);
}
public static void WriteAsync(this Stream stream, byte[] data, Action<Result<CompletionSignal>> resultHandler)
{
Request<CompletionSignal>.HandleResponse((Func<byte[], int, int, AsyncCallback, object, IAsyncResult>)stream.BeginWrite, data, 0, data.Length, (Action<IAsyncResult>)stream.EndWrite, resultHandler);
}
public static void WriteAndDisposeAsync(this Stream stream, byte[] data, Action<Result<CompletionSignal>> resultHandler)
{
Request<CompletionSignal>.HandleResponse(stream.BeginWrite, data, 0, data.Length, delegate(IAsyncResult ar)
{
try
{
stream.EndWrite(ar);
return CompletionSignal.Instance;
}
finally
{
stream?.Dispose();
}
}, resultHandler);
}
public static string Convert(this string value, Encoding originalEncoding, Encoding newEncoding)
{
byte[] valueBytes = value.GetBytes(originalEncoding);
byte[] newBytes = Encoding.Convert(originalEncoding, newEncoding, valueBytes);
return newEncoding.GetString(newBytes);
}
public static byte[] GetBytes(this string value)
{
return value.GetBytes(new ASCIIEncoding());
}
public static byte[] GetBytes(this string value, Encoding encoding)
{
return encoding.GetBytes(value);
}
public static bool IsEven(this int value)
{
return value % 2 == 0;
}
public static bool IsOdd(this int value)
{
return !value.IsEven();
}
public static int[] IndexesOf(this string s, char value)
{
List<int> indexes = new List<int>();
for (int i = s.IndexOf(value); i > -1; i = s.IndexOf(value, i + 1))
{
indexes.Add(i);
}
return indexes.ToArray();
}
public static string ParsedSubString(this string text, string startToken, string endToken, bool startWithLastOccurrences)
{
return text.ParsedSubString(startToken, endToken, includeStartToken: false, includeEndToken: false, startWithLastOccurrences);
}
public static string ParsedSubString(this string text, string startToken, string endToken)
{
return text.ParsedSubString(startToken, endToken, includeStartToken: false, includeEndToken: false, startWithLastOccurrences: false);
}
public static string ParsedSubString(this string text, string startToken, string endToken, bool includeStartToken, bool includeEndToken, bool startWithLastOccurrences)
{
int startIndex = ((!startWithLastOccurrences) ? text.ToLower().IndexOf(startToken.ToLower()) : text.ToLower().LastIndexOf(startToken.ToLower()));
startIndex = ((startIndex >= 0) ? (startIndex + ((!includeStartToken) ? startToken.Length : 0)) : 0);
text = text.Substring(startIndex);
int length = text.ToLower().IndexOf(endToken.ToLower());
length = ((length >= 0) ? (length + (includeEndToken ? endToken.Length : 0)) : text.Length);
return text.Substring(0, length);
}
public static void GetRequestStreamAsync(this WebRequest webRequest, Action<Result<Stream>> resultHandler)
{
Request<Stream>.HandleResponse(webRequest.BeginGetRequestStream, webRequest.EndGetRequestStream, resultHandler);
}
public static IAsyncResult BeginGetResponseStream(this WebResponse webResponse, AsyncCallback callback, object state)
{
FastAsyncResult<Stream> result = new FastAsyncResult<Stream>(callback, state);
RobloxThreadPool.QueueUserWorkItem(delegate
{
try
{
result.SetCompleted(webResponse.GetResponseStream());
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception completed)
{
result.SetCompleted(completed);
}
});
return result;
}
public static Stream EndGetResponseStream(this WebResponse webResponse, IAsyncResult asyncResult)
{
using FastAsyncResult<Stream> result = asyncResult as FastAsyncResult<Stream>;
return result.GetResult();
}
public static void GetResponseStreamAsync(this WebResponse webResponse, Action<Result<Stream>> resultHandler)
{
Request<Stream>.HandleResponse(webResponse.BeginGetResponseStream, webResponse.EndGetResponseStream, resultHandler);
}
public static IAsyncResult BeginLoad(this XmlDocument xmlDocument, Stream data, AsyncCallback callback, object state)
{
FastAsyncResult result = new FastAsyncResult(callback, state);
RobloxThreadPool.QueueUserWorkItem(delegate
{
try
{
xmlDocument.Load(data);
result.SetCompleted();
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception completed)
{
result.SetCompleted(completed);
}
});
return result;
}
public static void EndLoad(this XmlDocument _, IAsyncResult asyncResult)
{
using FastAsyncResult result = asyncResult as FastAsyncResult;
if (result.Error != null)
{
throw result.Error;
}
}
public static void LoadAsync(this XmlDocument xmlDocument, Stream data, Action<Result<CompletionSignal>> resultHandler)
{
Request<CompletionSignal>.HandleResponse(xmlDocument.BeginLoad, data, delegate(IAsyncResult ar)
{
xmlDocument.EndLoad(ar);
return CompletionSignal.Instance;
}, resultHandler);
}
public static string ToQuery(this string query)
{
return query.ToQuery(QueryOperators.AND);
}
public static string ToQuery(this string query, QueryOperators defaultOperator)
{
string op = $" {defaultOperator.ToString()} ";
string q = "";
if (!string.IsNullOrEmpty(query.Trim()))
{
string searchTerm = "";
char qout = '"';
char ws = ' ';
List<string> searchTerms = new List<string>();
bool isQuot = false;
query = Regex.Replace(query.Trim(), "\\s{2,}", ws.ToString());
char[] bytes = query.ToCharArray();
for (int i = 0; i < query.Length; i++)
{
char @char = bytes[i];
searchTerm += @char;
if (@char.Equals(qout))
{
isQuot = !isQuot;
}
if (isQuot)
{
continue;
}
if (@char.Equals(qout))
{
AddSearchTerm(searchTerm, ref searchTerms, defaultOperator);
searchTerm = "";
}
if (@char.Equals(ws))
{
if (!string.IsNullOrEmpty(searchTerm.Trim()))
{
AddSearchTerm(searchTerm, ref searchTerms, defaultOperator);
}
searchTerm = "";
}
}
if (searchTerm != "" && (!IsQueryOperator(searchTerm) || searchTerm.Equals(")")))
{
AddSearchTerm(searchTerm, ref searchTerms, defaultOperator);
}
if (!IsQueryOperator(searchTerms[0]))
{
q = searchTerms[0];
}
for (int j = 1; j < searchTerms.Count; j++)
{
if (!IsQueryOperator(searchTerms[j]) && !IsQueryOperator(searchTerms[j - 1]) && !searchTerms[j].Trim().Equals(")"))
{
q += op;
}
if (searchTerms[j].ToUpper() == QueryOperators.NOT.ToString() && !IsQueryOperator(searchTerms[j - 1]))
{
q += op;
}
q = q + " " + searchTerms[j];
}
q = Regex.Replace(q.Trim(), "\\s{2,}", ws.ToString());
}
return q;
}
private static bool IsQueryOperator(string s)
{
s = s.Trim().ToLower();
string[] names = Enum.GetNames(typeof(QueryOperators));
foreach (string name in names)
{
if (s == name.ToLower())
{
return true;
}
}
if (!(s == "("))
{
return s == ")";
}
return true;
}
private static string AddQuotesToSearchTerm(string searchTerm)
{
char qout = '"';
char openBkt = '(';
char closeBkt = ')';
searchTerm = searchTerm.Trim();
if (!IsQueryOperator(searchTerm) && searchTerm[0] == qout)
{
if (searchTerm[searchTerm.Length - 1] != qout)
{
searchTerm += qout;
}
else
{
searchTerm = ((searchTerm[0] != openBkt) ? (qout + searchTerm) : (openBkt + AddQuotesToSearchTerm(searchTerm.Substring(1))));
if (searchTerm[searchTerm.Length - 1] == closeBkt)
{
searchTerm = AddQuotesToSearchTerm(searchTerm.Substring(0, searchTerm.Length - 1)) + closeBkt;
}
else if (searchTerm[searchTerm.Length - 1] != qout)
{
searchTerm += qout;
}
}
}
return searchTerm;
}
private static void AddSearchTerm(string searchTerm, ref List<string> searchTerms, QueryOperators defaultOperator)
{
int idx = searchTerm.IndexOf(quote);
searchTerm = Regex.Replace(searchTerm, "^(\\&{1,}|\\+{1,})", " and ");
searchTerm = Regex.Replace(searchTerm, "^(\\|{1,})", " or ");
searchTerm = Regex.Replace(searchTerm, "^(\\~{1,})", " near ");
searchTerm = Regex.Replace(searchTerm, "^(\\-{1,}|\\!{1,})", $" {defaultOperator.ToString()} not ");
searchTerm = Regex.Replace(searchTerm.Trim(), "\\s+", space.ToString());
int length = searchTerm.Length;
if (searchTerm.Contains(quote.ToString()))
{
length = idx;
}
if (searchTerm.Substring(0, length).Contains(space.ToString()))
{
string[] array = searchTerm.Substring(0, length).Split(spaceDelimiter, StringSplitOptions.RemoveEmptyEntries);
foreach (string subTerm in array)
{
searchTerms.Add(AddQuotesToSearchTerm(subTerm));
}
if (length < searchTerm.Length)
{
searchTerms.Add(AddQuotesToSearchTerm(searchTerm.Substring(length)));
}
}
else
{
searchTerms.Add(AddQuotesToSearchTerm(searchTerm));
}
}
public static string ToDescription(this Enum value)
{
DescriptionAttribute[] array = (DescriptionAttribute[])value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), inherit: false);
if (array != null && array.Length != 0)
{
return array[0].Description;
}
return value.ToString();
}
public static bool UnorderedEqual<T>(this ICollection<T> list, ICollection<T> listToCompare)
{
if (list.Count != listToCompare.Count)
{
return false;
}
Dictionary<T, int> d = new Dictionary<T, int>();
foreach (T key in list)
{
if (d.TryGetValue(key, out var num2))
{
d[key] = num2 + 1;
}
else
{
d.Add(key, 1);
}
}
foreach (T key2 in listToCompare)
{
if (!d.TryGetValue(key2, out var num))
{
return false;
}
if (num == 0)
{
return false;
}
d[key2] = num - 1;
}
foreach (int value in d.Values)
{
if (value != 0)
{
return false;
}
}
return true;
}
public static string ToJSON<TSource>(this IEnumerable<TSource> source) where TSource : class
{
string json = "[";
foreach (TSource obj in source)
{
json = json + obj.ToJSON() + ",";
}
json = json.TrimEnd(',');
return json + "]";
}
public static string ToJSON<T>(this T obj) where T : class
{
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(T));
using MemoryStream stream = new MemoryStream();
dataContractJsonSerializer.WriteObject(stream, obj);
return Encoding.Default.GetString(stream.ToArray());
}
public static T FromJSON<T>(this string json) where T : class
{
using MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(json));
return new DataContractJsonSerializer(typeof(T)).ReadObject(stream) as T;
}
}
@@ -0,0 +1,112 @@
using System;
using System.Threading;
namespace Roblox.Common;
public class FastAsyncResult : IAsyncResult, IDisposable
{
private Exception _Error;
private readonly AsyncCallback _Callback;
private bool _IsCompleted = true;
private readonly object _State;
private ManualResetEvent _WaitHandle;
public WaitHandle AsyncWaitHandle => CreateWaitHandle();
public object AsyncState => _State;
public bool CompletedSynchronously => false;
public bool IsCompleted => _IsCompleted;
public Exception Error => _Error;
public FastAsyncResult(AsyncCallback callback, object state)
{
_Callback = callback;
_State = state;
}
private WaitHandle CreateWaitHandle()
{
if (_WaitHandle != null)
{
return _WaitHandle;
}
ManualResetEvent resetEvt = new ManualResetEvent(initialState: false);
if (Interlocked.CompareExchange(ref _WaitHandle, resetEvt, null) != null)
{
resetEvt.Close();
}
if (_IsCompleted)
{
_WaitHandle.Set();
}
return _WaitHandle;
}
public void Dispose()
{
_WaitHandle?.Close();
}
public void SetCompleted()
{
_IsCompleted = true;
Thread.MemoryBarrier();
_WaitHandle?.Set();
_Callback?.Invoke(this);
}
public void SetCompleted(Exception error)
{
_Error = error;
SetCompleted();
}
public void SetFailed(Exception error)
{
_Error = error;
}
}
public class FastAsyncResult<T> : FastAsyncResult, IResult<T>
{
private T result;
[Obsolete("This property can throw, which is bad design. Use GetResult() and SetCompleted() instead")]
public T Token
{
get
{
return GetResult();
}
set
{
result = value;
}
}
public FastAsyncResult(AsyncCallback callback, object state)
: base(callback, state)
{
}
public T GetResult()
{
if (base.Error != null)
{
throw base.Error;
}
return result;
}
public void SetCompleted(T result)
{
this.result = result;
SetCompleted();
}
}
@@ -0,0 +1,137 @@
using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Ccr.Core;
namespace Roblox.Common;
public class FileHelper : IDisposable
{
private static readonly int _DefaultPoolSize;
private static readonly DispatcherQueue _DispatcherQueue;
private static readonly Port<FileHelper> _Pool;
private FileHelper()
{
}
static FileHelper()
{
_DefaultPoolSize = 25;
_DispatcherQueue = new PatchedDispatcherQueue("Roblox FileHelper", new Dispatcher(0, ThreadPriority.Normal, DispatcherOptions.UseBackgroundThreads, "Roblox FileHelper"));
_Pool = new Port<FileHelper>();
Thread thread = new Thread(MonitorPerformance);
thread.IsBackground = true;
thread.Name = "Performance Monitor: FileHelper";
thread.Start();
for (int i = 0; i < _DefaultPoolSize; i++)
{
AddToPool();
}
}
public static void ExecuteTask<TResult>(Func<TResult> func, PortSet<TResult, Exception> result)
{
Port<FileHelper> port = new Port<FileHelper>();
Get(port);
CcrService.Singleton.Activate<Receiver<FileHelper>>(Arbiter.Receive(persist: false, port, delegate(FileHelper fh)
{
try
{
result.Post(func());
}
catch (Exception item)
{
result.Post(item);
}
finally
{
fh.Dispose();
}
}));
}
public static void ExecuteTask<Arg0, TResult>(Func<Arg0, TResult> func, Arg0 arg0, PortSet<TResult, Exception> result)
{
Port<FileHelper> port = new Port<FileHelper>();
Get(port);
CcrService.Singleton.Activate<Receiver<FileHelper>>(Arbiter.Receive(persist: false, port, delegate(FileHelper fh)
{
try
{
result.Post(func(arg0));
}
catch (Exception item)
{
result.Post(item);
}
finally
{
fh.Dispose();
}
}));
}
public static void AddToPool()
{
_Pool.Post(new FileHelper());
}
private static void Get(Port<FileHelper> result)
{
Arbiter.Activate(_DispatcherQueue, Arbiter.Receive(persist: false, _Pool, delegate(FileHelper h)
{
result.Post(h);
}));
}
private static void MonitorPerformance()
{
try
{
string categoryName = "Roblox FileHelper";
if (!PerformanceCounterCategory.Exists(categoryName))
{
CounterCreationDataCollection collection = new CounterCreationDataCollection();
collection.Add(new CounterCreationData("Dispatcher Queue Count", string.Empty, PerformanceCounterType.NumberOfItems32));
collection.Add(new CounterCreationData("Dispatcher Queue Current Scheduling Rate", string.Empty, PerformanceCounterType.RateOfCountsPerSecond64));
collection.Add(new CounterCreationData("Dispatcher Queue Scheduled Task Count", string.Empty, PerformanceCounterType.NumberOfItems64));
collection.Add(new CounterCreationData("Dispatcher Pending Task Count", string.Empty, PerformanceCounterType.NumberOfItems32));
collection.Add(new CounterCreationData("Dispatcher Processed Task Count", string.Empty, PerformanceCounterType.NumberOfItems64));
collection.Add(new CounterCreationData("Dispatcher Worker Thread Count", string.Empty, PerformanceCounterType.NumberOfItems32));
PerformanceCounterCategory.Create(categoryName, string.Empty, PerformanceCounterCategoryType.SingleInstance, collection);
}
PerformanceCounter perfDispatcherQueueCount = new PerformanceCounter(categoryName, "Dispatcher Queue Count", readOnly: false);
PerformanceCounter perfDispatcherQueueCurrentSchedulingRate = new PerformanceCounter(categoryName, "Dispatcher Queue Current Scheduling Rate", readOnly: false);
PerformanceCounter perfDispatcherQueueScheduledTaskCount = new PerformanceCounter(categoryName, "Dispatcher Queue Scheduled Task Count", readOnly: false);
PerformanceCounter perfDispatcherPendingTaskCount = new PerformanceCounter(categoryName, "Dispatcher Pending Task Count", readOnly: false);
PerformanceCounter perfDispatcherProcessedTaskCount = new PerformanceCounter(categoryName, "Dispatcher Processed Task Count", readOnly: false);
PerformanceCounter perfDispatcherWorkerThreadCount = new PerformanceCounter(categoryName, "Dispatcher Worker Thread Count", readOnly: false);
long num = _DispatcherQueue.ScheduledTaskCount;
while (true)
{
perfDispatcherQueueCount.RawValue = _DispatcherQueue.Count;
long scheduledTaskCount = _DispatcherQueue.ScheduledTaskCount;
perfDispatcherQueueCurrentSchedulingRate.IncrementBy(scheduledTaskCount - num);
num = (perfDispatcherQueueScheduledTaskCount.RawValue = scheduledTaskCount);
perfDispatcherPendingTaskCount.RawValue = _DispatcherQueue.Dispatcher.PendingTaskCount;
perfDispatcherProcessedTaskCount.RawValue = _DispatcherQueue.Dispatcher.ProcessedTaskCount;
perfDispatcherWorkerThreadCount.RawValue = _DispatcherQueue.Dispatcher.WorkerThreadCount;
}
}
catch (ThreadAbortException)
{
}
catch (Exception ex)
{
ExceptionHandler.LogException(ex);
}
}
public void Dispose()
{
AddToPool();
}
}
@@ -0,0 +1,5 @@
namespace Roblox.Common;
public abstract class IJSONizable
{
}
@@ -0,0 +1,6 @@
namespace Roblox.Common;
public interface IResult<T>
{
T GetResult();
}
@@ -0,0 +1,145 @@
using System.Text;
using System.Web.Script.Serialization;
namespace Roblox.Common;
public class JSON
{
public static string WriteError(string message)
{
return $"{{\"Error\" : \"{message}\"}}";
}
public static string WriteProperty(string propertyName, string propertyValue)
{
return $"{{\"{propertyName}\" : \"{propertyValue}\"}}";
}
public static string AddListToJSONObject(string jsonString, string propertyName, string propertyValue)
{
if (!jsonString.Contains("{") && !jsonString.Contains("}") && !jsonString.Contains("[") && !jsonString.Contains("]"))
{
jsonString = "{" + jsonString + "}";
}
bool isArray = false;
if (jsonString.StartsWith("[") && jsonString.EndsWith("]"))
{
isArray = true;
jsonString = jsonString.Remove(0, 1);
jsonString = jsonString.Remove(jsonString.Length - 1);
}
string property = $"\"{propertyName}\" : {propertyValue}";
if (!isArray)
{
jsonString = jsonString.Remove(jsonString.Length - 1);
if (jsonString.Length != 1)
{
jsonString += ",";
}
jsonString = jsonString + property + "}";
}
else
{
if (jsonString.Length != 0)
{
jsonString += ",";
}
jsonString += property;
}
if (isArray)
{
jsonString = "[" + jsonString + "]";
}
return jsonString;
}
public static string AddObjectToJSONObject(string jsonString, string propertyName, string propertyValue)
{
if (!jsonString.Contains("{") && !jsonString.Contains("}") && !jsonString.Contains("[") && !jsonString.Contains("]"))
{
jsonString = "{" + jsonString + "}";
}
bool isArray = false;
if (jsonString.StartsWith("[") && jsonString.EndsWith("]"))
{
isArray = true;
jsonString = jsonString.Remove(0, 1);
jsonString = jsonString.Remove(jsonString.Length - 1);
}
string property = $"\"{propertyName}\" : {propertyValue}";
if (!isArray)
{
jsonString = jsonString.Remove(jsonString.Length - 1);
if (jsonString.Length != 1)
{
jsonString += ",";
}
jsonString = jsonString + property + "}";
}
else
{
if (jsonString.Length != 0)
{
jsonString += ",";
}
jsonString += property;
}
if (isArray)
{
jsonString = "[" + jsonString + "]";
}
return jsonString;
}
public static string AddPropertyToJSONObject(string jsonString, string propertyName, string propertyValue)
{
if (!jsonString.Contains("{") && !jsonString.Contains("}") && !jsonString.Contains("[") && !jsonString.Contains("]"))
{
jsonString = "{" + jsonString + "}";
}
bool isArray = false;
if (jsonString.StartsWith("[") && jsonString.EndsWith("]"))
{
isArray = true;
jsonString = jsonString.Remove(0, 1);
jsonString = jsonString.Remove(jsonString.Length - 1);
}
string property = $"\"{propertyName}\" : \"{propertyValue}\"";
if (!isArray)
{
jsonString = jsonString.Remove(jsonString.Length - 1);
if (jsonString.Length != 1)
{
jsonString += ",";
}
jsonString = jsonString + property + "}";
}
else
{
if (jsonString.Length != 0)
{
jsonString += ",";
}
jsonString += property;
}
if (isArray)
{
jsonString = "[" + jsonString + "]";
}
return jsonString;
}
}
public abstract class Json
{
protected string SerializedData;
public virtual string Serialize()
{
if (SerializedData == null)
{
string value = new JavaScriptSerializer().Serialize(this);
SerializedData = value.Convert(Encoding.Unicode, Encoding.UTF8);
}
return SerializedData;
}
}
@@ -0,0 +1,26 @@
namespace Roblox.Common;
public static class MailHeaders
{
public const string Bcc = "bcc";
public const string Cc = "cc";
public const string Date = "date";
public const string From = "from";
public const string Importance = "importance";
public const string InReplyTo = "in-reply-to";
public const string MessageId = "message-id";
public const string Received = "received";
public const string ReplyTo = "reply-to";
public const string Subject = "subject";
public const string To = "to";
}
@@ -0,0 +1,217 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Mail;
using System.Net.Mime;
using System.Text.RegularExpressions;
using Roblox.Common.Mime;
namespace Roblox.Common;
public class MailMessageEx : MailMessage
{
public const string EmailRegexPattern = "(['\"]{1,}.+['\"]{1,}\\s+)?<?[\\w\\.\\-]+@[^\\.][\\w\\.\\-]+\\.[a-z]{2,}>?";
private long _octets;
private int _messageNumber;
private static readonly char[] AddressDelimiters = new char[2] { ',', ';' };
private List<MailMessageEx> _children;
public long Octets
{
get
{
return _octets;
}
set
{
_octets = value;
}
}
public int MessageNumber
{
get
{
return _messageNumber;
}
internal set
{
_messageNumber = value;
}
}
public List<MailMessageEx> Children => _children;
public string PlainTextBody
{
get
{
if (ContentType.MediaType == MediaTypes.TextPlain)
{
return base.Body;
}
foreach (AlternateView view in base.AlternateViews)
{
if (view.ContentType.MediaType == MediaTypes.TextPlain)
{
StreamReader rdr = new StreamReader(view.ContentStream);
return rdr.ReadToEnd();
}
}
return "";
}
}
public DateTime DeliveryDate
{
get
{
string date = GetHeader("date");
if (string.IsNullOrEmpty(date))
{
return DateTime.MinValue;
}
return Convert.ToDateTime(date);
}
}
public MailAddress ReturnAddress
{
get
{
string replyTo = GetHeader("reply-to");
if (string.IsNullOrEmpty(replyTo))
{
return null;
}
return CreateMailAddress(replyTo);
}
}
public string Routing => GetHeader("received");
public string MessageId => GetHeader("message-id");
public string ReplyToMessageId => GetHeader("in-reply-to", stripBrackts: true);
public string MimeVersion => GetHeader("mime-version");
public string ContentId => GetHeader("content-id");
public string ContentDescription => GetHeader("content-description");
public ContentDisposition ContentDisposition
{
get
{
string contentDisposition = GetHeader("content-disposition");
if (string.IsNullOrEmpty(contentDisposition))
{
return null;
}
return new ContentDisposition(contentDisposition);
}
}
public ContentType ContentType
{
get
{
string contentType = GetHeader("content-type");
if (string.IsNullOrEmpty(contentType))
{
return null;
}
return MimeReader.GetContentType(contentType);
}
}
public MailMessageEx()
{
_children = new List<MailMessageEx>();
}
private string GetHeader(string header)
{
return GetHeader(header, stripBrackts: false);
}
private string GetHeader(string header, bool stripBrackts)
{
if (stripBrackts)
{
return MimeEntity.TrimBrackets(base.Headers[header]);
}
return base.Headers[header];
}
public static MailMessageEx CreateMailMessageFromEntity(MimeEntity entity)
{
MailMessageEx message = new MailMessageEx();
string[] allKeys = entity.Headers.AllKeys;
foreach (string key in allKeys)
{
string value = entity.Headers[key];
if (value.Equals(string.Empty))
{
value = " ";
}
message.Headers.Add(key.ToLowerInvariant(), value);
switch (key.ToLowerInvariant())
{
case "bcc":
PopulateAddressList(value, message.Bcc);
break;
case "cc":
PopulateAddressList(value, message.CC);
break;
case "from":
message.From = CreateMailAddress(value);
break;
case "reply-to":
message.ReplyTo = CreateMailAddress(value);
break;
case "subject":
message.Subject = value;
break;
case "to":
PopulateAddressList(value, message.To);
break;
}
}
return message;
}
public static MailAddress CreateMailAddress(string address)
{
try
{
return new MailAddress(address.Trim('\t'));
}
catch (FormatException e)
{
throw new ApplicationException("Unable to create mail address from provided string: " + address, e);
}
}
public static void PopulateAddressList(string addressList, MailAddressCollection recipients)
{
foreach (MailAddress address in GetMailAddresses(addressList))
{
recipients.Add(address);
}
}
public static IEnumerable<MailAddress> GetMailAddresses(string addressList)
{
Regex email = new Regex("(['\"]{1,}.+['\"]{1,}\\s+)?<?[\\w\\.\\-]+@[^\\.][\\w\\.\\-]+\\.[a-z]{2,}>?");
foreach (Match match in email.Matches(addressList))
{
yield return CreateMailAddress(match.Value);
}
}
}
@@ -0,0 +1,38 @@
using Microsoft.Win32;
namespace Roblox.Common;
public class MimeTypes
{
public static string GetExtensionFromMime(string mimeType)
{
try
{
RegistryKey regKey = Registry.ClassesRoot.OpenSubKey("Mime\\Database\\Content Type\\" + mimeType, writable: false);
if (regKey == null)
{
return null;
}
string ext = regKey.GetValue("Extension") as string;
if (string.IsNullOrEmpty(ext))
{
return string.Empty;
}
return ext;
}
catch
{
return string.Empty;
}
}
public static string GetMimeFromExtension(string ext)
{
RegistryKey regKey = Registry.ClassesRoot.OpenSubKey(ext, writable: false);
if (regKey == null)
{
return null;
}
return regKey.GetValue("Content Type") as string;
}
}
@@ -0,0 +1,111 @@
using System;
using System.Threading;
namespace Roblox.Common;
public class NonReentrantTimer : MarshalByRefObject, IDisposable
{
private Timer _timer;
private TimerCallback _callback;
private TimerCallback _skipExecutionCallback;
private int _count;
public TimerCallback SkipExecutionCallback
{
get
{
return _skipExecutionCallback;
}
set
{
_skipExecutionCallback = value;
}
}
private NonReentrantTimer(TimerCallback callback)
{
_callback = callback;
_count = 0;
_skipExecutionCallback = null;
}
public NonReentrantTimer(TimerCallback callback, object state, int dueTime, int period)
: this(callback)
{
_timer = new Timer(InternalCallback, state, dueTime, period);
}
public NonReentrantTimer(TimerCallback callback, object state, long dueTime, long period)
: this(callback)
{
_timer = new Timer(InternalCallback, state, dueTime, period);
}
public NonReentrantTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
: this(callback)
{
_timer = new Timer(InternalCallback, state, dueTime, period);
}
public NonReentrantTimer(TimerCallback callback, object state, uint dueTime, uint period)
: this(callback)
{
_timer = new Timer(InternalCallback, state, dueTime, period);
}
public bool Change(int dueTime, int period)
{
return _timer.Change(dueTime, period);
}
public bool Change(long dueTime, long period)
{
return _timer.Change(dueTime, period);
}
public bool Change(TimeSpan dueTime, TimeSpan period)
{
return _timer.Change(dueTime, period);
}
public bool Change(uint dueTime, uint period)
{
return _timer.Change(dueTime, period);
}
private void InternalCallback(object state)
{
try
{
if (Interlocked.Increment(ref _count) == 1)
{
_callback(state);
}
else
{
_skipExecutionCallback?.Invoke(state);
}
}
finally
{
Interlocked.Decrement(ref _count);
}
}
public bool Dispose(WaitHandle notifyObject)
{
if (_timer != null)
{
return _timer.Dispose(notifyObject);
}
return false;
}
public void Dispose()
{
_timer?.Dispose();
}
}
@@ -0,0 +1,57 @@
using System;
using System.Diagnostics;
namespace Roblox.Common;
public class ObjectCounter<T>
{
private class Count
{
private static readonly string perfCategory = "Roblox.Common.ObjectCounter";
private readonly PerformanceCounter perfItemCount;
private readonly PerformanceCounter perfCollectRate;
public Count()
{
if (!PerformanceCounterCategory.Exists(perfCategory))
{
CounterCreationDataCollection counterCreationDataCollection = new CounterCreationDataCollection
{
new CounterCreationData("Count", string.Empty, PerformanceCounterType.NumberOfItems32),
new CounterCreationData("Collect Rate", string.Empty, PerformanceCounterType.RateOfCountsPerSecond32)
};
PerformanceCounterCategory.Create(perfCategory, string.Empty, PerformanceCounterCategoryType.MultiInstance, counterCreationDataCollection);
}
string typeName = typeof(T).Name;
Console.WriteLine($"ObjectCounter name: {typeName}");
perfItemCount = new PerformanceCounter(perfCategory, "Count", readOnly: false);
perfItemCount.RawValue = 0L;
perfCollectRate = new PerformanceCounter(perfCategory, "Collect Rate", readOnly: false);
}
internal void Increment()
{
perfItemCount.Increment();
}
internal void Decrement()
{
perfItemCount.Decrement();
perfCollectRate.Increment();
}
}
private static readonly Count count = new Count();
public ObjectCounter()
{
count.Increment();
}
~ObjectCounter()
{
count.Decrement();
}
}
@@ -0,0 +1,119 @@
using System;
using System.Threading;
using Microsoft.Ccr.Core;
namespace Roblox.Common;
public class RefreshAhead<T> : IDisposable
{
private DateTime _LastRefresh = DateTime.MinValue;
private readonly Timer _RefreshTimer;
private T _Value;
public TimeSpan IntervalSinceRefresh => DateTime.Now.Subtract(_LastRefresh);
public T Value => _Value;
private RefreshAhead(T initialValue, TimeSpan refreshInterval, Func<T> refreshDelegate)
{
RefreshAhead<T> refreshAhead = this;
int refreshIntervalInMilliseconds = (int)refreshInterval.TotalMilliseconds;
_Value = initialValue;
_LastRefresh = DateTime.Now;
_RefreshTimer = new Timer(delegate
{
refreshAhead.Refresh(refreshDelegate);
}, null, refreshIntervalInMilliseconds, refreshIntervalInMilliseconds);
}
private RefreshAhead(T initialValue, TimeSpan refreshInterval, Action<PortSet<T, Exception>> refreshDelegate)
{
RefreshAhead<T> refreshAhead = this;
int refreshIntervalInMilliseconds = (int)refreshInterval.TotalMilliseconds;
_Value = initialValue;
_LastRefresh = DateTime.Now;
_RefreshTimer = new Timer(delegate
{
refreshAhead.Refresh(refreshDelegate);
}, null, refreshIntervalInMilliseconds, refreshIntervalInMilliseconds);
}
public RefreshAhead(TimeSpan refreshInterval, Func<T> refreshDelegate)
{
RefreshAhead<T> refreshAhead = this;
int refreshIntervalInMilliseconds = (int)refreshInterval.TotalMilliseconds;
_RefreshTimer = new Timer(delegate
{
refreshAhead.Refresh(refreshDelegate);
}, null, refreshIntervalInMilliseconds, refreshIntervalInMilliseconds);
}
private void Refresh(Func<T> refreshDelegate)
{
try
{
_Value = refreshDelegate();
_LastRefresh = DateTime.Now;
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception ex)
{
ExceptionHandler.LogException(ex);
}
}
private void Refresh(Action<PortSet<T, Exception>> refreshDelegate)
{
try
{
PortSet<T, Exception> valueResult = new PortSet<T, Exception>();
refreshDelegate(valueResult);
CcrService.Singleton.Choice(valueResult, delegate(T value)
{
_Value = value;
_LastRefresh = DateTime.Now;
});
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception ex)
{
ExceptionHandler.LogException(ex);
}
}
public static RefreshAhead<T> ConstructAndPopulate(TimeSpan refreshInterval, Func<T> refreshDelegate)
{
T value = refreshDelegate();
return new RefreshAhead<T>(value, refreshInterval, refreshDelegate);
}
public static void ConstructAndPopulate(TimeSpan refreshInterval, Action<PortSet<T, Exception>> refreshDelegate, PortSet<RefreshAhead<T>, Exception> result)
{
PortSet<T, Exception> valueResult = new PortSet<T, Exception>();
refreshDelegate(valueResult);
CcrService.Singleton.Choice(valueResult, delegate(T value)
{
RefreshAhead<T> item = new RefreshAhead<T>(value, refreshInterval, refreshDelegate);
result.Post(item);
}, delegate(Exception ex)
{
result.Post(ex);
});
}
public void Dispose()
{
if (_RefreshTimer != null)
{
_RefreshTimer.Dispose();
}
}
}
@@ -0,0 +1,77 @@
using System;
namespace Roblox.Common;
public class Request<T>
{
public static Result<CompletionSignal> GetResponse(Action<IAsyncResult> responseHandler, IAsyncResult asyncResult)
{
try
{
responseHandler(asyncResult);
return new Result<CompletionSignal>(CompletionSignal.Instance);
}
catch (Exception ex)
{
return new Result<CompletionSignal>(ex);
}
}
public static Result<T> GetResponse(Func<IAsyncResult, T> responseHandler, IAsyncResult asyncResult)
{
try
{
return new Result<T>(responseHandler(asyncResult));
}
catch (Exception ex)
{
return new Result<T>(ex);
}
}
public static void HandleResponse(Func<AsyncCallback, object, IAsyncResult> beginHandler, Action<IAsyncResult> endHandler, Action<Result<CompletionSignal>> resultHandler)
{
beginHandler(delegate(IAsyncResult ar)
{
resultHandler(GetResponse(endHandler, ar));
}, null);
}
public static void HandleResponse<Arg0>(Func<Arg0, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Action<IAsyncResult> endHandler, Action<Result<CompletionSignal>> resultHandler)
{
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, c, s), endHandler, resultHandler);
}
public static void HandleResponse<Arg0, Arg1>(Func<Arg0, Arg1, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Arg1 arg1, Action<IAsyncResult> endHandler, Action<Result<CompletionSignal>> resultHandler)
{
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, arg1, c, s), endHandler, resultHandler);
}
public static void HandleResponse<Arg0, Arg1, Arg2>(Func<Arg0, Arg1, Arg2, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Arg1 arg1, Arg2 arg2, Action<IAsyncResult> endHandler, Action<Result<CompletionSignal>> resultHandler)
{
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, arg1, arg2, c, s), endHandler, resultHandler);
}
public static void HandleResponse(Func<AsyncCallback, object, IAsyncResult> beginHandler, Func<IAsyncResult, T> endHandler, Action<Result<T>> resultHandler)
{
beginHandler(delegate(IAsyncResult ar)
{
resultHandler(GetResponse(endHandler, ar));
}, null);
}
public static void HandleResponse<Arg0>(Func<Arg0, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Func<IAsyncResult, T> endHandler, Action<Result<T>> resultHandler)
{
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, c, s), endHandler, resultHandler);
}
public static void HandleResponse<Arg0, Arg1>(Func<Arg0, Arg1, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Arg1 arg1, Func<IAsyncResult, T> endHandler, Action<Result<T>> resultHandler)
{
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, arg1, c, s), endHandler, resultHandler);
}
public static void HandleResponse<Arg0, Arg1, Arg2>(Func<Arg0, Arg1, Arg2, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Arg1 arg1, Arg2 arg2, Func<IAsyncResult, T> endHandler, Action<Result<T>> resultHandler)
{
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, arg1, arg2, c, s), endHandler, resultHandler);
}
}
@@ -0,0 +1,70 @@
using System;
namespace Roblox.Common;
public class Result<T>
{
private Exception _Exception;
private T _Value;
public Exception Exception => _Exception;
public T Value
{
get
{
if (_Exception != null)
{
throw _Exception;
}
return _Value;
}
}
public Result(T value)
{
_Value = value;
}
public Result(Exception exception)
{
_Exception = exception;
}
public void Test(Action<T> successHandler, Action<Exception> failureHandler)
{
try
{
if (_Exception != null)
{
throw _Exception;
}
successHandler?.Invoke(_Value);
}
catch (Exception ex)
{
failureHandler?.Invoke(ex);
}
}
public void Test(Action<T> successHandler, Action<Exception> failureHandler, Action cleanupHandler)
{
try
{
if (_Exception != null)
{
throw _Exception;
}
successHandler?.Invoke(_Value);
}
catch (Exception ex)
{
failureHandler?.Invoke(ex);
}
finally
{
cleanupHandler?.Invoke();
}
}
}
@@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Roblox.Common;
public class RoundRobin<T>
{
private readonly T[] _Candidates;
private int _CurrentIndex;
public RoundRobin(IEnumerable<T> elements)
{
_Candidates = elements.ToArray();
}
public T Next()
{
int index = Interlocked.Increment(ref _CurrentIndex);
if (index >= _Candidates.Length)
{
_CurrentIndex = 0;
return _Candidates[0];
}
return _Candidates[index];
}
}
@@ -0,0 +1,97 @@
using System;
using System.Threading;
namespace Roblox.Common;
public class SynchronousCompletionAsyncResult : IAsyncResult
{
private Exception _Error;
private readonly AsyncCallback _Callback;
private bool _IsCompleted = true;
private readonly object _State;
public WaitHandle AsyncWaitHandle
{
get
{
throw new NotImplementedException();
}
}
public object AsyncState => _State;
public bool CompletedSynchronously => true;
public bool IsCompleted => _IsCompleted;
public Exception Error => _Error;
public SynchronousCompletionAsyncResult(AsyncCallback callback, object state)
{
_Callback = callback;
_State = state;
}
public SynchronousCompletionAsyncResult(AsyncCallback callback, object state, bool setComplete)
{
_Callback = callback;
_State = state;
if (setComplete)
{
SetCompleted();
}
}
public SynchronousCompletionAsyncResult(AsyncCallback callback, object state, Exception error)
{
_Callback = callback;
_State = state;
SetCompleted(error);
}
public void CheckResult()
{
if (_Error != null)
{
throw _Error;
}
}
public void SetCompleted()
{
_IsCompleted = true;
_Callback?.Invoke(this);
}
public void SetCompleted(Exception error)
{
_Error = error;
SetCompleted();
}
}
public class SynchronousCompletionAsyncResult<T> : SynchronousCompletionAsyncResult, IResult<T>
{
private T result;
[Obsolete("This property can throw, which is bad design. Use GetResult() and SetCompleted() instead")]
public T Token => GetResult();
public SynchronousCompletionAsyncResult(T token, AsyncCallback callback, object state)
: base(callback, state)
{
result = token;
SetCompleted();
}
public T GetResult()
{
if (base.Error != null)
{
throw base.Error;
}
return result;
}
}
@@ -0,0 +1,16 @@
using System.Web;
namespace Roblox.Common;
public class TextTransforms
{
public static string transformString(string stringToTransform)
{
return performCarriageReturnSubstitution(HttpContext.Current.Server.HtmlEncode(stringToTransform));
}
public static string performCarriageReturnSubstitution(string text)
{
return text.Replace("\n", "<br />");
}
}
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
namespace Roblox.Common;
public class VisitsFloodCheckBuffer : IDisposable
{
private readonly ObjectRegistry<int, IDictionary<long, DateTime>> userLogs;
private static readonly TimeSpan floodCheckInterval = TimeSpan.FromHours(1.0);
public static readonly VisitsFloodCheckBuffer Singleton = new VisitsFloodCheckBuffer();
private VisitsFloodCheckBuffer()
{
userLogs = new ObjectRegistry<int, IDictionary<long, DateTime>>(new ObjectRegistry<int, IDictionary<long, DateTime>>.Configuration("VisitsFloodCheckBuffer")
{
Lease = floodCheckInterval,
Getter = (int k) => new Dictionary<long, DateTime>(),
PurgeFrequency = TimeSpan.FromMinutes(5.0)
});
}
public bool PassesFloodCheck(int userId, long placeId)
{
if (!userLogs.TryGetValue(userId, out var log))
{
return true;
}
IDictionary<long, DateTime> sync = log;
lock (sync)
{
if (log == null)
{
return true;
}
DateTime minValue;
return !log.TryGetValue(placeId, out minValue) || minValue == DateTime.MinValue || DateTime.Now.Subtract(minValue) > floodCheckInterval;
}
}
public bool RegisterVisit(int userId, long placeId)
{
DateTime now = DateTime.Now;
IDictionary<long, DateTime> dict = userLogs.Get(userId);
IDictionary<long, DateTime> sync = dict;
KeyValuePair<long, DateTime> item = new KeyValuePair<long, DateTime>(placeId, now);
lock (sync)
{
if (dict.TryGetValue(placeId, out var minValue))
{
if (!(now.Subtract(minValue) > floodCheckInterval))
{
dict.Remove(placeId);
dict.Add(new KeyValuePair<long, DateTime>(placeId, DateTime.MinValue));
return false;
}
dict.Remove(placeId);
}
dict.Add(item);
return true;
}
}
public void Dispose()
{
userLogs.Dispose();
}
}
@@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace Roblox.Common;
public static class XMLUtil
{
public static string GenerateXMLTable(ICollection<KeyValuePair<object, object>> entries)
{
string text = "<Value><Table>";
foreach (KeyValuePair<object, object> entry in entries)
{
text += $"<Entry><Key>{entry.Key.ToString()}</Key><Value>{entry.Value.ToString()}</Value></Entry>";
}
return text + "</Table></Value>";
}
public static string GenerateXMLBool(bool value)
{
return $"<List><Value>{value.ToString()}</Value></List>";
}
}
@@ -0,0 +1,20 @@
using System.Text;
using System.Xml;
namespace Roblox.Common;
public class Xml : XmlBase
{
private static XmlWriterSettings _XmlWriterSettings = new XmlWriterSettings
{
Encoding = Encoding.UTF8
};
public static Xml Singleton = new Xml();
protected override XmlWriterSettings XmlWriterSettings => _XmlWriterSettings;
private Xml()
{
}
}
@@ -0,0 +1,51 @@
using System;
using System.IO;
using System.Text;
using System.Xml;
namespace Roblox.Common;
public abstract class XmlBase
{
protected abstract XmlWriterSettings XmlWriterSettings { get; }
private bool IsLegalCharacter(int character)
{
if (character != 9 && character != 10 && character != 13 && (character < 32 || character > 55295) && (character < 57344 || character > 65533))
{
if (character >= 65536)
{
return character <= 1114111;
}
return false;
}
return true;
}
public string Sanitize(string xmlString)
{
if (string.IsNullOrEmpty(xmlString))
{
return xmlString;
}
StringBuilder builder = new StringBuilder(xmlString.Length);
foreach (char c in xmlString)
{
if (IsLegalCharacter(c))
{
builder.Append(c);
}
}
return builder.ToString();
}
public virtual byte[] Write(Action<XmlWriter> write)
{
using MemoryStream ms = new MemoryStream();
using XmlWriter writer = XmlWriter.Create(ms, XmlWriterSettings);
write(writer);
writer.Flush();
ms.Seek(0L, SeekOrigin.Begin);
return ms.ToArray();
}
}